需要在考虑SQL中的重叠值时自动进行排序

时间:2013-12-17 23:42:23

标签: sql

我正面临一个具有挑战性的要求,让我在键盘上敲打我的头。我需要实现一个脚本,该脚本将对数据集进行排序和汇总,同时考虑与不同标识符关联的重叠值。我选择的表包含以下列:

  • BoxNumber(需要按此值分组,作为标识符)
  • ProdBeg(包含文档/记录的第一个“页码”)
  • ProdEnd(包含文档/记录的最后“页码”)
  • DateProduced(文档生成日期)
  • ArtifactID(每个文档的唯一标识符)
  • NumPages(包含与每个文档关联的页数)

选择没有条件的数据样本类似于以下内容(抱歉糟糕的格式化):

BoxNumber | ProdBeg | ProdEnd | DateProduced | ArtifactID | NumPages

1200 | ABC01 | ABC10 | 12/4/2013 | 1564589 | 10

1201 | ABC11 | ABC20 | 12/4/2013 | 1498658 | 10

1200 | ABC21 | ABC30 | 12/4/2013 | 1648596 | 10

1200 | ABC31 | ABC40 | 12/4/2013 | 1489535 | 10

使用类似下面的内容进行有效分组并按箱号对数据进行排序,同时考虑不同的DateProduced日期,但不考虑不同BoxNumbers之间重叠的ProdBeg / ProdEnd值:

SELECT BoxNumber, MIN(ProdBeg) AS 'ProdBeg', MAX(ProdEnd) AS 'ProdEnd', DateProduced, COUNT(ArtifactID) AS 'Documents', SUM(NumPages) AS 'Pages'
FROM MyTable
GROUP BY BoxNumber, DateProduced
ORDER BY ProdBeg, ProdEnd

这会产生:

BoxNumber | ProdBeg | ProdEnd| DateProduced | Documents| Pages
1200 | ABC01 | ABC40 | 12/4/2013 | 3 | 30
1201 | ABC11 | ABC20 | 12/4/2013 | 1 | 10

这里,显而易见的是,框1201的ProdBeg / ProdEnd值与框1200的重叠。上述脚本的变化不起作用,因为它将固有地忽略任何重叠并且仅选择最小/最大。我们需要能产生以下结果的东西:

BoxNumber | ProdBeg | ProdEnd | DateProduced | Documents| Pages
1200 | ABC01 | ABC10 | 12/4/2013 | 1 | 10
1201 | ABC11 | ABC20 | 12/4/2013 | 1 | 10
1200 | ABC21 | ABC40 | 12/4/2013 | 2 | 20

我只是不确定我们如何按箱号分组而不显示不同的值(这可能导致ProdBeg / ProdEnd的重叠)。任何建议将不胜感激!环境版本是SQL 2008 R2(SP1)。

1 个答案:

答案 0 :(得分:0)

Yuch。至少如果您在SQL Server 2012中有lead() / lag(),这将有所帮助。但它是可行的。

这个想法如下:

  1. 添加一个变量,即代码的 number 部分(最后两位数字)。
  2. 计算序列中的下一个数字。
  3. 如果与下一个号码存在间隙,则计算一个标志。这是“团体”的开始。
  4. 计算“组开始”标志的累积总和。这是一个群组ID。
  5. 进行聚合。
  6. 以下查询遵循此逻辑。我没有包括生成的日期。除非一个盒子可以在多天出现,否则这个数字似乎是多余的。 (添加生成的日期只是将条件添加到where子句。)生成的查询是:

    with bp as (
          select t.*,
                 cast(right(prodbeg, 2) as int) as pbeg,
                 cast(right(prodend, 2) as int) as pend
          from mytable t
         ),
         bp1 as (
          select bp.*,
                 (select top 1 pbeg
                  from bp bp2
                  where bp2.pbeg < bp.pbeg and pb2.BoxNumber = pb.BoxNumber
                  order by bp2.pbeg desc
                 ) as prevpend
          from bp
         ),
         bp2 as (
          select bp1.*,
                 (select sum(case when prevpend = pbeg - 1 then 0 else 1 end)
                  from bp1 bp1a
                  where bp1a.pbeg < bp1.pbeg and pb1a.BoxNumber = pb1.BoxNumber
                 ) as groupid
          from bp1
         )
    select BoxNumber, MIN(ProdBeg) AS ProdBeg, MAX(ProdEnd) AS ProdEnd, DateProduced,
           COUNT(ArtifactID) AS 'Documents', SUM(NumPages) AS 'Pages'
    FROM bp2
    GROUP BY BoxNumber, groupid
    ORDER BY ProdBeg, ProdEnd;