从表中选择连续范围

发布于 2025-01-04 04:57:10 字数 482 浏览 2 评论 0原文

我需要根据连续数字(N 列)和这些数字相关的相同“类别”(下面的 C 列)从表中提取连续范围。从图形上看,它看起来像这样:

 N  C  D
--------
 1  x  a           C  N1  N2  D1  D2
 2  x  b          ------------------
 3  x  c           x   1   4   a   d     (continuous range with same N)
 4  x  d    ==>    x   6   7   e   f     (new range because "5" is missing)
 6  x  e           y   8  10   g   h     (new range because C changed to "y")
 7  x  f
 8  y  g
 9  y  h
10  y  i

SQL Server 是 2005。谢谢。

I need to extract continous ranges from a table based on consecutive numbers (column N) and same "category" these numbers relate to (column C below). Graphically it looks like this:

 N  C  D
--------
 1  x  a           C  N1  N2  D1  D2
 2  x  b          ------------------
 3  x  c           x   1   4   a   d     (continuous range with same N)
 4  x  d    ==>    x   6   7   e   f     (new range because "5" is missing)
 6  x  e           y   8  10   g   h     (new range because C changed to "y")
 7  x  f
 8  y  g
 9  y  h
10  y  i

SQL Server is 2005. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

时光与爱终年不遇 2025-01-11 04:57:10
DECLARE @myTable Table
(
    N INT,
    C CHAR(1),
    D CHAR(1)
)
INSERT INTO @myTable(N,C,D) VALUES(1,  'x', 'a');
INSERT INTO @myTable(N,C,D) VALUES(2,  'x', 'b');
INSERT INTO @myTable(N,C,D) VALUES(3,  'x', 'c');
INSERT INTO @myTable(N,C,D) VALUES(4,  'x', 'd');
INSERT INTO @myTable(N,C,D) VALUES(6,  'x', 'e');
INSERT INTO @myTable(N,C,D) VALUES(7,  'x', 'f');
INSERT INTO @myTable(N,C,D) VALUES(8,  'y', 'g');
INSERT INTO @myTable(N,C,D) VALUES(9,  'y', 'h');
INSERT INTO @myTable(N,C,D) VALUES(10, 'y', 'i');


WITH StartingPoints AS(

    SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
    FROM @myTable AS A
    WHERE NOT EXISTS(
        SELECT *
        FROM @myTable B
        WHERE B.C = A.C
          AND B.N = A.N - 1
    )
 ),
 EndingPoints AS(
    SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
    FROM @myTable AS A
    WHERE NOT EXISTS (
        SELECT *
        FROM @myTable B
        WHERE B.C = A.C
          AND B.N = A.N + 1
    )
 ) 
SELECT StartingPoints.C,
       StartingPoints.N AS [N1],
       EndingPoints.N AS [N2],
       StartingPoints.D AS [D1],
       EndingPoints.D AS [D2] 
FROM StartingPoints
JOIN EndingPoints ON StartingPoints.rownum = EndingPoints.rownum

结果

C    N1          N2          D1   D2
---- ----------- ----------- ---- ----
x    1           4           a    d
x    6           7           e    f
y    8           10          g    i
DECLARE @myTable Table
(
    N INT,
    C CHAR(1),
    D CHAR(1)
)
INSERT INTO @myTable(N,C,D) VALUES(1,  'x', 'a');
INSERT INTO @myTable(N,C,D) VALUES(2,  'x', 'b');
INSERT INTO @myTable(N,C,D) VALUES(3,  'x', 'c');
INSERT INTO @myTable(N,C,D) VALUES(4,  'x', 'd');
INSERT INTO @myTable(N,C,D) VALUES(6,  'x', 'e');
INSERT INTO @myTable(N,C,D) VALUES(7,  'x', 'f');
INSERT INTO @myTable(N,C,D) VALUES(8,  'y', 'g');
INSERT INTO @myTable(N,C,D) VALUES(9,  'y', 'h');
INSERT INTO @myTable(N,C,D) VALUES(10, 'y', 'i');


WITH StartingPoints AS(

    SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
    FROM @myTable AS A
    WHERE NOT EXISTS(
        SELECT *
        FROM @myTable B
        WHERE B.C = A.C
          AND B.N = A.N - 1
    )
 ),
 EndingPoints AS(
    SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
    FROM @myTable AS A
    WHERE NOT EXISTS (
        SELECT *
        FROM @myTable B
        WHERE B.C = A.C
          AND B.N = A.N + 1
    )
 ) 
SELECT StartingPoints.C,
       StartingPoints.N AS [N1],
       EndingPoints.N AS [N2],
       StartingPoints.D AS [D1],
       EndingPoints.D AS [D2] 
FROM StartingPoints
JOIN EndingPoints ON StartingPoints.rownum = EndingPoints.rownum

Results:

C    N1          N2          D1   D2
---- ----------- ----------- ---- ----
x    1           4           a    d
x    6           7           e    f
y    8           10          g    i
单身情人 2025-01-11 04:57:10

RANK 函数比 ROW_NUMBER 更安全,以防任何 N 值重复,如下例所示:

declare @ncd table(N int, C char, D char);

insert into @ncd
select 1,'x','a' union all
select 2,'x','b' union all
select 3,'x','c' union all
select 4,'x','d' union all
select 4,'x','e' union all
select 7,'x','f' union all
select 8,'y','g' union all
select 9,'y','h' union all
select 10,'y','i' union all
select 10,'y','j';

with a as (
    select *
    , r = N-rank()over(partition by C order by N)
    from @ncd
)
select C=MIN(C)
, N1=MIN(N)
, N2=MAX(N)
, D1=MIN(D)
, D2=MAX(D)
from a
group by r;

Result,它正确地承受了重复的 4 和 10:

C    N1          N2          D1   D2
---- ----------- ----------- ---- ----
x    1           4           a    e
x    7           7           f    f
y    8           10          g    j

The RANK function is a safer bet than ROW_NUMBER, in case any N values are duplicated, as in the following example:

declare @ncd table(N int, C char, D char);

insert into @ncd
select 1,'x','a' union all
select 2,'x','b' union all
select 3,'x','c' union all
select 4,'x','d' union all
select 4,'x','e' union all
select 7,'x','f' union all
select 8,'y','g' union all
select 9,'y','h' union all
select 10,'y','i' union all
select 10,'y','j';

with a as (
    select *
    , r = N-rank()over(partition by C order by N)
    from @ncd
)
select C=MIN(C)
, N1=MIN(N)
, N2=MAX(N)
, D1=MIN(D)
, D2=MAX(D)
from a
group by r;

Result, which correctly withstands the duplicated 4 and 10:

C    N1          N2          D1   D2
---- ----------- ----------- ---- ----
x    1           4           a    e
x    7           7           f    f
y    8           10          g    j
写给空气的情书 2025-01-11 04:57:10

使用这个答案作为起始点,我最终得到以下结果:

;
WITH data (N, C, D) AS (
  SELECT 1,  'x', 'a' UNION ALL
  SELECT 2,  'x', 'b' UNION ALL
  SELECT 3,  'x', 'c' UNION ALL
  SELECT 4,  'x', 'd' UNION ALL
  SELECT 6,  'x', 'e' UNION ALL
  SELECT 7,  'x', 'f' UNION ALL
  SELECT 8,  'y', 'g' UNION ALL
  SELECT 9,  'y', 'h' UNION ALL
  SELECT 10, 'y', 'i'
),
ranked AS (
  SELECT
    curr.*,
    Grp     = curr.N - ROW_NUMBER() OVER (PARTITION BY curr.C ORDER BY curr.N),
    IsStart = CASE WHEN pred.C IS NULL THEN 1 ELSE 0 END,
    IsEnd   = CASE WHEN succ.C IS NULL THEN 1 ELSE 0 END
  FROM data AS curr
    LEFT JOIN data AS pred ON curr.C = pred.C AND curr.N = pred.N + 1
    LEFT JOIN data AS succ ON curr.C = succ.C AND curr.N = succ.N - 1
)
SELECT
  C,
  N1 = MIN(N),
  N2 = MAX(N),
  D1 = MAX(CASE IsStart WHEN 1 THEN D END),
  D2 = MAX(CASE IsEnd   WHEN 1 THEN D END)
FROM ranked
WHERE 1 IN (IsStart, IsEnd)
GROUP BY C, Grp

Using this answer as a starting point, I ended up with the following:

;
WITH data (N, C, D) AS (
  SELECT 1,  'x', 'a' UNION ALL
  SELECT 2,  'x', 'b' UNION ALL
  SELECT 3,  'x', 'c' UNION ALL
  SELECT 4,  'x', 'd' UNION ALL
  SELECT 6,  'x', 'e' UNION ALL
  SELECT 7,  'x', 'f' UNION ALL
  SELECT 8,  'y', 'g' UNION ALL
  SELECT 9,  'y', 'h' UNION ALL
  SELECT 10, 'y', 'i'
),
ranked AS (
  SELECT
    curr.*,
    Grp     = curr.N - ROW_NUMBER() OVER (PARTITION BY curr.C ORDER BY curr.N),
    IsStart = CASE WHEN pred.C IS NULL THEN 1 ELSE 0 END,
    IsEnd   = CASE WHEN succ.C IS NULL THEN 1 ELSE 0 END
  FROM data AS curr
    LEFT JOIN data AS pred ON curr.C = pred.C AND curr.N = pred.N + 1
    LEFT JOIN data AS succ ON curr.C = succ.C AND curr.N = succ.N - 1
)
SELECT
  C,
  N1 = MIN(N),
  N2 = MAX(N),
  D1 = MAX(CASE IsStart WHEN 1 THEN D END),
  D2 = MAX(CASE IsEnd   WHEN 1 THEN D END)
FROM ranked
WHERE 1 IN (IsStart, IsEnd)
GROUP BY C, Grp
白龙吟 2025-01-11 04:57:10

写一个存储过程。它将创建并填充一个包含 C、N1、N2、D1 和 D2 列的临时表。

  • 创建临时表,
  • 使用游标循环遍历包含按 N 排序的 N、C、D 的表中的条目,
  • 使用变量检测新范围 (Ni < N(i-1)-1) 并存储 N1、N2, D1 和 D2
  • 插入到检测到的每个范围的临时表中(检测到的新范围或光标的和)

请告诉我您是否需要代码示例。

Write a stored procedure. It will create and fill a temporary table witch will contain C, N1, N2, D1 and D2 columns.

  • Create the temporary table
  • use a cursor to loop on entries in table containing N, C, D ordered by N
  • use a variable to detect a new range (Ni < N(i-1)-1) and to store N1, N2, D1 and D2
  • INSERT into the temporary table for each range detected (new range detected or and of the cursor)

Tell me if you need a code example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文