【问题标题】:Gaps and island fails with 3 columns using SQL Server使用 SQL Server 的间隙和孤岛因 3 列而失败
【发布时间】:2018-07-03 16:02:49
【问题描述】:

我遇到了间隙和孤岛解决方案的奇怪行为。有 3 列(第 3 列是非整数),结果确实是随机的。假设我们有以下查询:

Declare @Table1 TABLE
(
    ID varchar(50), 
    yr float, 
    CO1 varchar(50)
);

INSERT INTO @Table1 (ID, yr, CO1)
VALUES ('I2','2011','ABE'), ('I2','2012','ABE'), ('I2','2013','ABE'),
       ('I2','2014','ABE'), ('I2','2014','ABE'), ('I2','2005','ABD'),
       ('I2','2006','ABD'), ('I2','2007','ABD'), ('I2','2008','ABD'),
       ('I2','2007','ABA CD'), ('I2','2011','ABA CD'), ('I2','2013','ABA CD');

SELECT 
    ID, CO1, StartSeqNo = MIN(yr), EndSeqNo = MAX(yr)
FROM 
    (SELECT 
         ID, yr, CO1,
         rn = yr - ROW_NUMBER() OVER (PARTITION BY ID ORDER BY yr)
     FROM 
         @Table1) a
GROUP BY 
    ID, CO1, rn ;

我想要的结果是:

ID  CO1    StartSeqNo   EndSeqNo
----------------------------
I2  ABA CD    2007       2007
I2  ABA CD    2011       2011
I2  ABA CD    2013       2013
I2  ABD       2005       2008
I2  ABE       2011       2014

我查看了 stackoverflow 和其他地方,以确定我是否遗漏了什么。我已经尝试过 distinct 和 dense_rank,都没有给出正确的结果

这是我已经尝试过的 distinct 和 dense_rank 查询:

--- distinct 

SELECT distinct ID,CO1, StartSeqNo=MIN(yr), EndSeqNo=MAX(yr)
FROM (
    SELECT distinct ID, yr, CO1
        ,rn=yr-ROW_NUMBER() OVER (PARTITION BY ID ORDER BY yr)
    FROM @Table1) a
GROUP BY ID, CO1, rn ;

--- with dense_rank
SELECT ID,CO1, StartSeqNo=MIN(yr), EndSeqNo=MAX(yr)
FROM (
    SELECT ID, yr, CO1
        ,rn=yr-dense_rank() OVER (PARTITION BY ID ORDER BY yr)
    FROM @Table1) a
GROUP BY ID, CO1, rn ;

我不明白为什么间隙和孤岛查询不适用于非整数列。我认为在某处分组存在问题。请帮我解决一下这个。

模拟

【问题讨论】:

    标签: sql sql-server gaps-and-islands gaps-in-data


    【解决方案1】:

    您需要DENSE_RANK,因为您有多个具有相同 ID/yr 组合的行,您需要将 CO1 添加到 PARTITION BY

    SELECT 
        ID, CO1, StartSeqNo = MIN(yr), EndSeqNo = MAX(yr)
    FROM 
        (SELECT 
             ID, yr, CO1,
             rn = yr - dense_rank() OVER (PARTITION BY ID, CO1 ORDER BY yr)
         FROM 
             @Table1) a
    GROUP BY 
        ID, CO1, rn ;
    

    【讨论】:

      【解决方案2】:

      如果没有间隔,年份将是每个 ID/CO1 组中的连续编号,您可以将其与无间隔编号进行比较,当然对于按年份排序的每个 ID/CO1 也必须连续编号。所以,如果你不 ORDER BY CO1(前年),你还必须在行编号函数中使用 CO1 来 PARTITION BY。 此外,您的数据包含重复的行,因此要在 ID/CO1 组中赋予相同的年份相同的数字,请使用 RANK 函数而不是 ROW_NUMBER:

      WITH a (ID, CO1, yr, nmbr) AS (
        SELECT ID, CO1, yr
          , yr - RANK() OVER (PARTITION BY ID, CO1 ORDER BY yr)
        FROM @Table1
      )
      SELECT ID, CO1, StartSeqNo = MIN(yr), EndSeqNo = MAX(yr)
      FROM a
      GROUP BY ID, CO1, nmbr;
      

      最后让我建议使用 int 而不是 float 作为年份数字。

      【讨论】:

      • RANK 不起作用,必须改为DENSE_RANK,即2014-2014-2015 是连续的,但排名的结果将是1-1-3 而不是1-1-2
      • @dnoeth 是的,你是对的,必须使用 DENSE_RANK 函数,我不知道 RANK 函数本身会产生间隙。有了 2015 年 ABE 的额外样本记录,我当然会注意到这一点。谢谢!
      【解决方案3】:

      你似乎想要:

      select id, co1, min(yr), max(yr)
      from (select *, (case when max(grp) over(partition by co1) > 1 then grp else 1 end) as grp1
            from (select *, yr - lag(yr, 1, yr) over (partition by id, co1 order by yr) as grp
                  from table
                 ) t
             ) t
      group by id, co1, grp1;
      

      【讨论】:

      • 不幸的是,这对 Yogesh 不起作用。您提出的解决方案“ABA CD 2007 2013”​​。我需要 2007、2011、2013 年的 ABA CD
      • @Simran。 . .哦,是的,您可以使用lag() 函数来检查年份差距并使用累积方法。
      猜你喜欢
      • 1970-01-01
      • 2021-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-05
      • 1970-01-01
      • 2022-09-27
      • 2015-05-03
      相关资源
      最近更新 更多