对连续表数据进行分组

时间:2018-01-23 22:09:38

标签: sql-server

TABLE 数据

start   end   status
1000    1002    Y
1003    1020    Y
1021    1022    N
1023    1030    Y
1031    1040    Y
1041    1050    Y
1051    1052    N
1053    1100    Y

我想将GROUP查询结果如下

start   end   status
1000    1020    Y
1021    1022    N
1023    1050    Y
1051    1052    N
1053    1100    Y

我尝试使用CTE递归但是在大约37500次迭代后最大化。该表有数十万个元组。

2 个答案:

答案 0 :(得分:0)

我在这个网站上找到了类似的解决方案。但是没有保存链接。相信那个家伙。无论如何,这是一个使用来自未知人的解决方案来获得你想要的结果的方法...

http://sqlfiddle.com/#!18/3e6b1/12/0

它基本上涉及多个自我连接和自我检查以获得我们想要的结果。

以下是代码:

create table tab1(
  id int identity(1,1) not null,
  startn int not null,
  endn int not null,
  status char(1)
);

insert into tab1(
  startn,
  endn,
  status
)
values
(1000,    1002,    'Y'),
(1003,    1020,    'Y'),
(1021,    1022,    'N'),
(1023,    1030,    'Y'),
(1031,    1040,    'Y'),
(1041,    1050,    'Y'),
(1051,    1052,    'N'),
(1053,    1100,    'Y')
;

设置

select
    t1.startn,
    min(t2.endn) as endn,
    t1.status
from tab1 as t1
join tab1 as t2
    on t1.startn <= t2.startn
    and t1.status = t2.status
    and not exists(
        select
            1
        from tab1 as t3
        where
           t2.endn + 1 >= t3.startn
           and t2.endn < t3.endn
           and t2.status = t3.status
    )
where
    not exists(
        select
            1
        from tab1 as t4
        where
            t1.startn > t4.startn
            and t1.startn <= t4.endn + 1
            and t1.status = t4.status
 )
group by t1.startn, t1.status

查询

答案 1 :(得分:0)

这是我使用CTE,ROW_NUMBER和LEAD

的解决方案

http://rextester.com/live/FMMU28874

http://sqlfiddle.com/#!18/c07b9/1

DECLARE @mytable TABLE (
  start_ int,
  end_ int,
  status_ char(1)
)

INSERT INTO @mytable
  VALUES (1000, 1002, 'Y'),
  (1003, 1020, 'Y'),
  (1021, 1022, 'N'),
  (1023, 1030, 'Y'),
  (1031, 1040, 'Y'),
  (1041, 1050, 'Y'),
  (1051, 1052, 'N'),
  (1053, 1100, 'Y')



;
WITH mycte    -- get next start and linear end when status changes
AS (SELECT
  *
FROM (SELECT
  iif(LEAD(status_, 1, status_) OVER (ORDER BY start_) <> status_,
  LEAD(start_, 1, start_) OVER (ORDER BY start_), 0) newstart,
  iif(LEAD(status_, 1, status_) OVER (ORDER BY start_) <> status_,
  end_, 0) newend
--,start_,end_, status_
FROM @mytable) x
WHERE x.newstart <> 0
AND x.newend <> 0),
myendcte   --- get max end and identify sequence to pair for the start
AS (SELECT
  newend,
  ROW_NUMBER() OVER (ORDER BY newend ASC) - 1 row_
FROM mycte
UNION
SELECT
  MAX(end_),
  (SELECT
    COUNT(1)
  FROM mycte)
FROM @mytable),
mystartcte -- get min start and identify sequence to pair for end
AS (SELECT
  newstart,
  ROW_NUMBER() OVER (ORDER BY newstart ASC) row_
FROM mycte
UNION
SELECT
  MIN(start_),
  0
FROM @mytable)
SELECT    --- final result
  newstart,
  newend,
  (SELECT
    status_
  FROM @mytable
  WHERE start_ = newstart)
  [Status]
FROM mystartcte s
INNER JOIN myendcte e
  ON e.row_ = s.row_

添加了解释