计算没有间隙的岛屿的大小

Count size of islands with no gaps

我的数据有 3 列; ID、名称、值 ID 是像行数一样的序号 我在数据中显示了 3 个不同的名称,但可能有 10 个以上 有 2 个 VALUE

我想以 'local' 方式对 NAME 和 VALUE 进行分组,return 这些 'local' 组的大小。这似乎是 Islands and Gaps 场景的一个版本,但没有重复值的 Gaps and Islands。我已经尝试了各种解决 Islands and Gaps 的方法,但到目前为止,没有真正的进展。

我尝试部分解决方案的失败尝试之一(Itzik Ben-Gan 的工作变体):

WITH A
AS (SELECT ROW_NUMBER() OVER (ORDER BY id) RN,
           NAME,
           VALUE
    FROM #data
    WHERE NAME LIKE '%Joe%'),
T
AS (
SELECT ROW_NUMBER() OVER (ORDER BY RN) - RN AS Grp,
           RN
    FROM A
    WHERE VALUE = 0)
SELECT MIN(T.RN) AS [From],
       MAX(T.RN) AS [To],
       MAX(T.RN) - MIN(T.RN) AS [Length]
FROM T
GROUP BY Grp
ORDER BY MIN(T.RN);

输出如下:

NAME VALUE Count
Joe 0 15
Joe 1 3
Joe 0 5
Joe 1 19
Sue 1 23
Sue 0 17
Sue 1 4
Mary 0 16
Mary 1 10
Mary 0 7
Mary 1 19
Mary 0 3

数据是这样的:

ID NAME VALUE
1 Joe 0
2 Joe 0
3 Joe 0
4 Joe 0
5 Joe 0
6 Joe 0
7 Joe 0
8 Joe 0
9 Joe 0
10 Joe 0
11 Sue 1
12 Sue 1
13 Sue 1
14 Sue 1
15 Sue 1
16 Sue 1
17 Sue 1
18 Sue 1
19 Sue 1
20 Sue 1
21 Sue 1
22 Sue 1
23 Sue 1
24 Sue 1
25 Sue 1
26 Sue 1
27 Mary 0
28 Mary 0
29 Mary 0
30 Mary 0
31 Mary 0
32 Mary 0
33 Mary 0
34 Mary 0
35 Joe 0
36 Joe 0
37 Joe 0
38 Joe 0
39 Joe 0
40 Joe 1
41 Joe 1
42 Joe 1
43 Joe 0
44 Joe 0
45 Joe 0
46 Joe 0
47 Joe 0
48 Joe 1
49 Joe 1
50 Mary 0
51 Mary 0
52 Mary 0
53 Mary 0
54 Mary 0
55 Mary 0
56 Mary 0
57 Mary 0
58 Mary 1
59 Mary 1
60 Mary 1
61 Mary 1
62 Mary 1
63 Mary 1
64 Mary 1
65 Mary 1
66 Mary 1
67 Mary 1
68 Mary 0
69 Sue 1
70 Sue 1
71 Sue 1
72 Sue 1
73 Sue 1
74 Sue 1
75 Sue 1
76 Sue 0
77 Sue 0
78 Sue 0
79 Sue 0
80 Sue 0
81 Sue 0
82 Sue 0
83 Sue 0
84 Mary 0
85 Mary 0
86 Mary 0
87 Mary 0
88 Mary 0
89 Mary 0
90 Mary 1
91 Mary 1
92 Mary 1
93 Mary 1
94 Mary 1
95 Mary 1
96 Mary 1
97 Mary 1
98 Mary 1
99 Mary 1
100 Mary 1
101 Sue 0
102 Sue 0
103 Sue 0
104 Sue 0
105 Sue 0
106 Sue 0
107 Sue 0
108 Sue 0
109 Sue 0
110 Sue 1
111 Sue 1
112 Sue 1
113 Sue 1
114 Joe 1
115 Joe 1
116 Joe 1
117 Joe 1
118 Joe 1
119 Joe 1
120 Joe 1
121 Joe 1
122 Joe 1
123 Joe 1
124 Mary 1
125 Mary 1
126 Mary 1
127 Mary 1
128 Mary 1
129 Mary 1
130 Mary 1
131 Mary 1
132 Mary 0
133 Mary 0
134 Mary 0
135 Joe 1
136 Joe 1
137 Joe 1
138 Joe 1
139 Joe 1
140 Joe 1
141 Joe 1
DROP TABLE IF EXISTS #data;
CREATE TABLE #data 
(
    ID INT,
    NAME VARCHAR(50),
    VALUE INT
);
INSERT INTO #data values
(1  ,'Joe', 0    ),
(2  ,'Joe', 0    ),
(3  ,'Joe', 0    ),
(4  ,'Joe', 0    ),
(5  ,'Joe', 0    ),
(6  ,'Joe', 0    ),
(7  ,'Joe', 0    ),
(8  ,'Joe', 0    ),
(9  ,'Joe', 0    ),
(10 ,'Joe', 0    ),
(11 ,'Sue', 1    ),
(12 ,'Sue', 1    ),
(13 ,'Sue', 1    ),
(14 ,'Sue', 1    ),
(15 ,'Sue', 1    ),
(16 ,'Sue', 1    ),
(17 ,'Sue', 1    ),
(18 ,'Sue', 1    ),
(19 ,'Sue', 1    ),
(20 ,'Sue', 1    ),
(21 ,'Sue', 1    ),
(22 ,'Sue', 1    ),
(23 ,'Sue', 1    ),
(24 ,'Sue', 1    ),
(25 ,'Sue', 1    ),
(26 ,'Sue', 1    ),
(27 ,'Mary',    0    ),
(28 ,'Mary',    0    ),
(29 ,'Mary',    0    ),
(30 ,'Mary',    0    ),
(31 ,'Mary',    0    ),
(32 ,'Mary',    0    ),
(33 ,'Mary',    0    ),
(34 ,'Mary',    0    ),
(35 ,'Joe', 0    ),
(36 ,'Joe', 0    ),
(37 ,'Joe', 0    ),
(38 ,'Joe', 0    ),
(39 ,'Joe', 0    ),
(40 ,'Joe', 1    ),
(41 ,'Joe', 1    ),
(42 ,'Joe', 1    ),
(43 ,'Joe', 0    ),
(44 ,'Joe', 0    ),
(45 ,'Joe', 0    ),
(46 ,'Joe', 0    ),
(47 ,'Joe', 0    ),
(48 ,'Joe', 1    ),
(49 ,'Joe', 1    ),
(50 ,'Mary',    0    ),
(51 ,'Mary',    0    ),
(52 ,'Mary',    0    ),
(53 ,'Mary',    0    ),
(54 ,'Mary',    0    ),
(55 ,'Mary',    0    ),
(56 ,'Mary',    0    ),
(57 ,'Mary',    0    ),
(58 ,'Mary',    1    ),
(59 ,'Mary',    1    ),
(60 ,'Mary',    1    ),
(61 ,'Mary',    1    ),
(62 ,'Mary',    1    ),
(63 ,'Mary',    1    ),
(64 ,'Mary',    1    ),
(65 ,'Mary',    1    ),
(66 ,'Mary',    1    ),
(67 ,'Mary',    1    ),
(68 ,'Mary',    0    ),
(69 ,'Sue', 1    ),
(70 ,'Sue', 1    ),
(71 ,'Sue', 1    ),
(72 ,'Sue', 1    ),
(73 ,'Sue', 1    ),
(74 ,'Sue', 1    ),
(75 ,'Sue', 1    ),
(76 ,'Sue', 0    ),
(77 ,'Sue', 0    ),
(78 ,'Sue', 0    ),
(79 ,'Sue', 0    ),
(80 ,'Sue', 0    ),
(81 ,'Sue', 0    ),
(82 ,'Sue', 0    ),
(83 ,'Sue', 0    ),
(84 ,'Mary',    0    ),
(85 ,'Mary',    0    ),
(86 ,'Mary',    0    ),
(87 ,'Mary',    0    ),
(88 ,'Mary',    0    ),
(89 ,'Mary',    0    ),
(90 ,'Mary',    1    ),
(91 ,'Mary',    1    ),
(92 ,'Mary',    1    ),
(93 ,'Mary',    1    ),
(94 ,'Mary',    1    ),
(95 ,'Mary',    1    ),
(96 ,'Mary',    1    ),
(97 ,'Mary',    1    ),
(98 ,'Mary',    1    ),
(99 ,'Mary',    1    ),
(100    ,'Mary',    1),
(101    ,'Sue', 0),
(102    ,'Sue', 0),
(103    ,'Sue', 0),
(104    ,'Sue', 0),
(105    ,'Sue', 0),
(106    ,'Sue', 0),
(107    ,'Sue', 0),
(108    ,'Sue', 0),
(109    ,'Sue', 0),
(110    ,'Sue', 1),
(111    ,'Sue', 1),
(112    ,'Sue', 1),
(113    ,'Sue', 1),
(114    ,'Joe', 1),
(115    ,'Joe', 1),
(116    ,'Joe', 1),
(117    ,'Joe', 1),
(118    ,'Joe', 1),
(119    ,'Joe', 1),
(120    ,'Joe', 1),
(121    ,'Joe', 1),
(122    ,'Joe', 1),
(123    ,'Joe', 1),
(124    ,'Mary',    1),
(125    ,'Mary',    1),
(126    ,'Mary',    1),
(127    ,'Mary',    1),
(128    ,'Mary',    1),
(129    ,'Mary',    1),
(130    ,'Mary',    1),
(131    ,'Mary',    1),
(132    ,'Mary',    0),
(133    ,'Mary',    0),
(134    ,'Mary',    0),
(135    ,'Joe', 1),
(136    ,'Joe', 1),
(137    ,'Joe', 1),
(138    ,'Joe', 1),
(139    ,'Joe', 1),
(140    ,'Joe', 1),
(141    ,'Joe', 1);

-- first find the changes
WITH breaks AS 
(
    SELECT ID, NAME, VALUE, 
        IIF(LAG(VALUE, 1) OVER (PARTITION BY Name ORDER BY ID) != VALUE, 1, 0) AS NewGrp 
    FROM #data
), 
-- then count how many breaks we have passed 
grouped AS 
(
    SELECT ID, NAME, VALUE, SUM(NewGrp) OVER (PARTITION BY Name ORDER BY ID) grp
    FROM breaks
)
-- group by that count
SELECT NAME, VALUE, COUNT(*)
FROM grouped
GROUP BY NAME, VALUE, grp
ORDER BY NAME, MIN(ID)