Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 将连续的非零行标记为不同的分区?_Sql_Sql Server_Window Functions - Fatal编程技术网

Sql 将连续的非零行标记为不同的分区?

Sql 将连续的非零行标记为不同的分区?,sql,sql-server,window-functions,Sql,Sql Server,Window Functions,假设我们有这个简单的模式和数据: DROP TABLE #builds CREATE TABLE #builds ( Id INT IDENTITY(1,1) NOT NULL, StartTime INT, IsPassed BIT ) INSERT INTO #builds (StartTime, IsPassed) VALUES (1, 1), (7, 1), (10, 0), (15, 1), (21, 1), (26, 0), (34, 0), (44, 0),

假设我们有这个简单的模式和数据:

DROP TABLE #builds
CREATE TABLE #builds (
    Id INT IDENTITY(1,1) NOT NULL,
    StartTime INT,
    IsPassed BIT
)
INSERT INTO #builds (StartTime, IsPassed) VALUES
(1, 1),
(7, 1),
(10, 0),
(15, 1),
(21, 1),
(26, 0),
(34, 0),
(44, 0),
(51, 1),
(60, 1)

SELECT StartTime, IsPassed, NextStartTime,
    CASE IsPassed WHEN 1 THEN 0 ELSE NextStartTime - StartTime END Duration
FROM (
    SELECT  
        LEAD(StartTime) OVER (ORDER BY StartTime) NextStartTime,
        StartTime, IsPassed
    FROM #builds
) x
ORDER BY StartTime
它生成以下结果集:

StartTime   IsPassed    NextStartTime   Duration
1           1           7               0
7           1           10              0
10          0           15              5
15          1           21              0
21          1           26              0
26          0           34              8
34          0           44              10
44          0           51              7
51          1           60              0
60          1           NULL            0
我需要汇总非零的连续持续时间值,并在批处理中第一行的开始时间显示它们。也就是说,我需要做到这一点:

StartTime   Duration
10          5
26          25
我就是不知道怎么做


PS:当然,真正的表包含更多的行。

这是一个间隙和孤岛问题,需要将
IsPassed
为常量的每个部分划分为不同的组。这可以通过计算整个表上的
ROW\u NUMBER()
IsPassed
分区的差值来实现。然后,您可以对
IsPassed=False
的每个组的
Duration
值进行
SUM
计算,并取
MIN(StartTime)
得出该组第一行的
StartTime

WITH CTE AS (
  SELECT StartTime, IsPassed,
         LEAD(StartTime) OVER (ORDER BY StartTime) AS NextStartTime
  FROM #builds
),
CTE2 AS (
  SELECT StartTime, IsPassed, NextStartTime,
         CASE IsPassed WHEN 1 THEN 0 ELSE NextStartTime - StartTime END Duration,
         ROW_NUMBER() OVER (ORDER BY StartTime) -
         ROW_NUMBER() OVER (PARTITION BY IsPassed ORDER BY StartTime) AS grp
  FROM CTE
)
SELECT MIN(StartTime) AS StartTime, SUM(Duration) AS Duration
FROM CTE2
WHERE IsPassed = 0
GROUP BY grp
ORDER BY MIN(StartTime)
输出:

StartTime   Duration
10          5
26          25

您的方法过于复杂。您只需将
0
s分配给恰好包含以下
1
的组

您可以通过计算每行上或每行后面的“1”的数量来实现这一点。当然,这也会为没有“0”的行分配一个分组。可通过确保每组中至少有on
0
来过滤这些内容:

select min(StartTime), max(startTime) - min(startTime)
from (select b.*,
             sum(case when IsPassed = 1 then 1 else 0 end) over (order by StartTime desc) as grp
      from builds b
     ) b
group by grp
having min(convert(int, IsPassed)) = 0
order by min(StartTime);
他是一把小提琴

或者另一种方法根本不使用聚合。它只需为每一行获取下一个“1”开始时间,然后向下过滤到第一个“0”行:


这可能是替代品中性能最好的。

这绝对是华丽的。我设法理解我的问题是一种缺口和孤岛(我在过去的几个小时里学会了这个词),但我没有理解理论,也没有弄清楚我该如何分组。“这太棒了!”马克我花了很长时间才弄明白。我在演示中留下了
SELECT*FROM CTE2
查询,以便您可以看到正在形成的组号。一旦你有了他们,得到结果就不难了。@mark我刚注意到我在第一次CTE中留下了一个不必要的
滞后
。我已经更新了答案和演示以删除它。所以他们的关键是对行进行分组,而我没有找到方法。我不明白在第一个变量中,
sum(当IsPassed=1,然后1,否则0结束)是如何通过(order by StartTime desc)
工作的。没有分区,
sum
到底做什么?我将很快研究第二种变体。您的回答突出了一个令人悲伤的事实——我不理解窗口函数是如何工作的。在第二个变体中,您再次使用min而不进行分区-它是如何工作的?@mark。如果没有按
划分的
分区,则所有行都在一个分区中。我认为答案解释了它们是如何工作的——它们使用窗口函数为数据分配适当的分组。这比您的方法或其他答案中的方法简单得多(可能更快)。
select StartTime, next_1_starttime - StartTime
from (select b.*,
             lag(IsPassed) over (order by StartTime) as prev_IsPassed,
             min(case when IsPassed = 1 then StartTime end) over (order by StartTime desc) as next_1_starttime
      from builds b
     ) b
where IsPassed = 0 and (prev_IsPassed = 1 or prev_IsPassed is null)
order by StartTime;