按块分组并获取每个-SQL的开始和结束时间
我在为可视化准备数据库输出时遇到问题 我有一个表(在Oracle SQL中),用于监视我网络上的每台计算机(第1列)所做的事情以及启动操作的时间(第2列)。见下文:按块分组并获取每个-SQL的开始和结束时间,sql,oracle,oracle-sqldeveloper,Sql,Oracle,Oracle Sqldeveloper,我在为可视化准备数据库输出时遇到问题 我有一个表(在Oracle SQL中),用于监视我网络上的每台计算机(第1列)所做的事情以及启动操作的时间(第2列)。见下文: # comps ~ 20 # actions - 10 # rows ~ 10_000 comp | time | action -------+--------+---------- comp_1 | t_0 | A comp_1 | t_1 | A comp_2 | t_2 |
# comps ~ 20
# actions - 10
# rows ~ 10_000
comp | time | action
-------+--------+----------
comp_1 | t_0 | A
comp_1 | t_1 | A
comp_2 | t_2 | B
comp_1 | t_3 | B
comp_1 | t_4 | A
comp_2 | t_5 | B
comp_2 | t_6 | B
comp_1 | t_7 | A
comp_1 | t_8 | A
comp_2 | t_9 | C
comp_2 | t_10 | C
comp_1 | t_11 | C
... ... ...
. . .
一个动作的结束就是另一个动作的开始。B从A开始到A结束。然后,在B结束后,A可能再次作为新的“块”出现。计算机一次只能做一个动作
时间值实际上是Oracledatetime
列(为了简化,它只写为t_x)
我需要帮助按计算机操作块对表进行分组,每个操作块都有开头和结尾t_start
是查询的开始时间(从),而t_end
是结束时间(到)
这是我想要的输出:
comp | action | start | end
-------+---------+---------+----------
comp_1 | A | t_start| t_3
comp_2 | B | t_2 | t_9
comp_1 | B | t_3 | t_4
comp_1 | A | t_4 | t_11
comp_2 | C | t_9 | t_end
comp_1 | C | t_11 | t_end
如果仔细观察,您会发现,源表中的记录可能是重叠的,因为每台计算机都是独立报告的,但都是按时间顺序排列的
我尝试了不同的版本,但没有成功。我怀疑,它的实现可能更聪明,或者通过使用“按拥有分组”或“结束”来实现,但我对SQL没有那么熟练
非常感谢您的光临。您可以通过模式匹配来实现这一点:
create table t (
comp varchar2(10),
time varchar2(10),
action varchar2(1)
);
insert into t values ( 'comp_1', 't_00', 'A' );
insert into t values ( 'comp_1', 't_01', 'A' );
insert into t values ( 'comp_2', 't_02', 'B' );
insert into t values ( 'comp_1', 't_03', 'B' );
insert into t values ( 'comp_1', 't_04', 'A' );
insert into t values ( 'comp_2', 't_05', 'B' );
insert into t values ( 'comp_2', 't_06', 'B' );
insert into t values ( 'comp_1', 't_07', 'A' );
insert into t values ( 'comp_1', 't_08', 'A' );
insert into t values ( 'comp_2', 't_09', 'C' );
insert into t values ( 'comp_2', 't_10', 'C' );
insert into t values ( 'comp_1', 't_11', 'C' );
commit;
select comp, st,
lead ( st ) over (
partition by comp
order by st
) en
from t
match_recognize (
partition by comp
order by time
measures
first ( time ) st
pattern ( init same* )
define
same as action = prev ( action )
);
COMP ST EN
comp_1 t_00 t_03
comp_1 t_03 t_04
comp_1 t_04 t_11
comp_1 t_11 <null>
comp_2 t_02 t_09
comp_2 t_09 <null>
创建表t(
comp varchar2(10),
时间varchar2(10),
行动2(1)
);
插入t值('comp_1','t_00','A');
插入t值('comp_1','t_01','A');
插入t值('comp_2','t_02','B');
插入t值('comp_1','t_03','B');
插入t值('comp_1','t_04','A');
插入t值('comp_2','t_05','B');
插入t值('comp_2','t_06','B');
插入t值('comp_1','t_07','A');
插入t值('comp_1','t_08','A');
插入t值('comp_2','t_09','C');
插入t值('comp_2','t_10','C');
插入t值('comp_1','t_11','C');
犯罪
选择comp,st,
领先(st)超过(
按comp划分
圣彼得堡
)嗯
从t
相配(
按comp划分
按时间订购
措施
第一次(时间)圣
模式(初始相同*)
定义
与action=prev(action)相同
);
公司
comp_1 t_00 t_03
组件1 t\u 03 t\u 04
comp_1 t_04 t_11
comp_1 t_11
comp_2 t_02 t_09
comp_2 t_09
在当前行的操作与前一行的操作相同(由分区和排序依据定义)时为true与action=prev(action)
- 模式是一个正则表达式,因此
是init same*
的一个实例,后跟零个或多个init
same
是未定义的,所以“始终为真”,匹配第一行,然后匹配操作与前一行不同的任何行init
- 选择中的
返回下一次开始的值lead
- 好的,我没想到我会有时间写这篇文章,因此发表了评论,但是:
让我们获取数据,根据每台计算机上前一行的操作是相同的还是不同的,将一列设置为0或1:
SELECT
t.*,
CASE WHEN action = LAG(action) OVER(PARTITION BY computer ORDER BY time) THEN 0 ELSE 1 END as differs
FROM
t
现在,让我们将0和1的流转换为递增计数器:
WITH cte AS(
SELECT
t.*,
CASE WHEN action = LAG(action) OVER(PARTITION BY comp ORDER BY time) THEN 0 ELSE 1 END as differs
FROM
t
)
SELECT cte.*, SUM(differs) OVER(PARTITION BY comp ORDER BY time ROWS UNBOUNDED PRECEDING) as run_tot_dif
现在让我们按每台计算机的计数器分组,并计算最小/最大时间。我注意到你的时间不是当前行动的结束,而是下一个行动的开始。为此,我们将在中添加一个函数,用于在第一个cte中为每台计算机拾取每行的下一次时间,以便我们以后可以拾取它,并且我们还将合并它,以便它在时间段结束时导致的空值变为:
WITH cte_diff AS(
SELECT
t.*,
COALESCE(LEAD(time) OVER(PARTITION BY comp ORDER BY time), :endtime) next_start_time,
CASE WHEN action = LAG(action) OVER(PARTITION BY comp ORDER BY time) THEN 0 ELSE 1 END as differs
FROM
t
WHERE time BETWEEN :starttime and :endtime --parameterize it
),
cte_runtot AS(
SELECT
cte_diff.*,
SUM(differs) OVER(PARTITION BY comp ORDER BY time ROWS UNBOUNDED PRECEDING) as run_tot_dif
FROM
cte_diff
)
SELECT
comp,
action,
MIN(time) as start_time,
MAX(next_start_time) as end_time
FROM
cte_runtot
GROUP BY
comp, action, run_tot_dif
ORDER BY
start_time
我要指出的是,我认为您在期望的输出中犯了一个错误—comp2的操作B一直运行到comp2在t=9时启动操作C,而不是直到comp1在t=7时启动操作A。。除非我误解了你的要求
聚苯乙烯;您可以通过不使用CTE将其折叠起来,但这可能并不意味着它更具可读性:
SELECT
comp,
action,
MIN(time) as start_time,
MAX(next_start) as end_time
FROM
(
SELECT
x.*,
SUM(diff) OVER(PARTITION BY comp ORDER BY time ROWS UNBOUNDED PRECEDING) as run_tot_diff
FROM
(
SELECT
t.*,
COALESCE(LEAD(time) OVER(PARTITION BY comp ORDER BY time), 9999) next_start,
CASE WHEN action = LAG(action) OVER(PARTITION BY comp ORDER BY time) THEN 0 ELSE 1 END diff
FROM t
WHERE time BETWEEN 0 and 11
) x
)y
GROUP BY
comp, action, run_tot_diff
ORDER BY
start_time
因为Oracle不允许在GROUP BY、其他窗口函数内部或聚合内部使用窗口函数(SQL Server对此问题较少)您如何知道comp 1在t3而不是t9(或8)完成了操作A?是因为它在3处做了一个B,然后又做了另一个a吗?有一种技术,你可以使用LAG来查看前一行的动作,按时间排序(但如果它确实是一个以t_为前缀的int,则需要将其转换为数字),并使用一个案例来查看它是否与当前行的动作不同,是放1,还是放0。然后,您可以使用带有行无界前置限定符的SUM OVER,将交替为1和0的这列转换为递增计数器,您可以按它分组。很高兴知道您的时间列是否真的是这样的,或者它是否是一些模糊的数据,而时间列实际上排序很像日期或时间谢谢你的回复。正如你所说,我要补充这些重要的细节(我的错)。查找其更新。感谢您的回复。我一定会调查这件事并报告我的发现。另外,我想,添加动作名称就像将其添加到select一样简单,对吗?哦,非常感谢。我马上就去试试。此外,您对comp2 t_7/t_9的看法是正确的。。应该是在9号。我会在OP中更正它。