基于标志递增计数的组记录

时间:2014-04-11 02:58:41

标签: sql-server sql-server-2008 tsql

拿两个......

实际数据:

division    ID          date            flag
ABC123      ZZZ123      1/17/2013       Y
ABC123      ZZZ123      1/25/2013       N
ABC123      ZZZ123      2/22/2013       Y
ABC123      ZZZ123      2/26/2013       N
ABC123      YYY222      3/20/2013       Y
ABC123      YYY222      5/17/2013       N
XYZ456      ZZZ999      1/15/2012       N
XYZ456      ZZZ999      1/30/2012       N
XYZ456      ZZZ123      2/09/2012       N
XYZ456      ZZZ123      4/13/2012       Y
XYZ456      ZZZ123      6/23/2012       N
XYZ456      ZZZ123      10/5/2012       Y
XYZ456      ZZZ123      11/18/2012      N

我需要构建一个新列ORDER_group,它将根据以下规则进行填充:

  • 每个分部和ID组合被视为" group",按日期排序,并且应该分配一个ORDER_group(以1开头)。
  • 每次"组"遇到" Y"的标志,它应该将ORDER_group增加1。
  • 如果"组"使用标志=" N"开始(具有最早日期的第一个记录),它仍应以ORDER_group = 1开始。
  • 如果"组"使用标志=" Y"开始(第一个记录最早的日期),它仍应以ORDER_group = 1开头。
  • 每个后续记录应该是相同的ORDER_group编号,除非新的"组"遇到(分区/ ID),它应该重置为1,或者下一个标志=" Y"遇到了。

预期结果:

division    ID          date            flag    ORDER_group
ABC123      ZZZ123      1/17/2013       Y       1
ABC123      ZZZ123      1/25/2013       N       1
ABC123      ZZZ123      2/22/2013       Y       2
ABC123      ZZZ123      2/26/2013       N       2
ABC123      YYY222      3/20/2013       Y       1
ABC123      YYY222      5/17/2013       N       1
XYZ456      ZZZ999      1/15/2012       N       1
XYZ456      ZZZ999      1/30/2012       N       1
XYZ456      ZZZ123      2/09/2012       N       1
XYZ456      ZZZ123      4/13/2012       Y       2
XYZ456      ZZZ123      6/23/2012       N       2
XYZ456      ZZZ123      10/5/2012       Y       3
XYZ456      ZZZ123      11/18/2012      N       3

理想情况下,这应该在没有循环/游标的情况下完成,除非有CTE /临时表的性能原因。填充此新列的最佳方法是什么?

非常感谢任何帮助。

实际数据的SQL Fiddler:http://sqlfiddle.com/#!3/5cca0/2

2 个答案:

答案 0 :(得分:1)

所以这是一种方法。它基于How do I calculate a running total in SQL without using a cursor?确实存在一些缺陷。我在建议中使用了一个索引,它使得排序工作 DESPITE 事实上不能保证更新的顺序。

对于Aaron Bertrand的治疗,它也值得你Calculate running total / running balance

这里可能聪明的位是将Y / N转换为1/0以用于计算。

CREATE TABLE Orders (division CHAR(6),ID CHAR(6),dat DATETIME, flag CHAR(1))
INSERT INTO Orders VALUES

('ABC123','ZZZ123','01/17/2013','Y')
,('ABC123','ZZZ123','01/25/2013','N')
,('ABC123','ZZZ123','01/25/2013','N')
,('ABC123','ZZZ123','01/25/2013','N')
,('ABC123','ZZZ123','01/25/2013','N')
,('ABC123','ZZZ123','02/22/2013','Y')
,('ABC123','ZZZ123','02/26/2013','N')
,('ABC123','YYY222','03/20/2013','Y')
,('ABC123','YYY222','05/17/2013','N')
,('XYZ456','ZZZ999','01/15/2012','N')
,('XYZ456','ZZZ999','01/30/2012','N')
,('XYZ456','ZZZ123','02/09/2012','N')
,('XYZ456','ZZZ123','04/13/2012','Y')
,('XYZ456','ZZZ123','06/23/2012','N')
,('XYZ456','ZZZ123','010/5/2012','Y')
,('XYZ456','ZZZ123','11/18/2012','N')


CREATE TABLE #Orders (division CHAR(6),    ID CHAR(6),   dat DATETIME, flag CHAR(1),flag_int INTEGER, rn BIGINT, OrderGroup INT)

CREATE CLUSTERED INDEX IDX_C_Temp_Order ON #Orders(division, id,rn)

INSERT INTO #Orders (division, id,dat,flag,flag_int,rn,OrderGroup)
SELECT division
      ,ID
      ,dat
      ,flag
      ,CASE flag WHEN 'y' THEN 1 ELSE 0 END flag_int
      ,ROW_NUMBER() OVER (PARTITION BY division, id ORDER BY dat) rn
      ,0 OrderGroup
  FROM Orders

DECLARE @OrderGroup INT = 0
UPDATE #Orders
   SET @OrderGroup = OrderGroup  = CASE WHEN rn = 1 THEN 1 ELSE @OrderGroup + flag_int END
  FROM #Orders

SELECT * 
  FROM #Orders
 ORDER BY division
         ,ID
         ,rn

DROP TABLE #Orders

答案 1 :(得分:0)

一个更简单的解决方案怎么样?

WITH Data AS (
    SELECT
        *,
        Num = Row_Number() OVER (PARTITION BY division, ID ORDER BY date)
    FROM MyTable
)
SELECT
    *
FROM
    Data D
    CROSS APPLY (
        SELECT Count(*)
        FROM Data D2
        WHERE
            D.division = D2.division
            AND D.ID = D2.ID
            AND D.date >= D2.date
            AND (D2.flag = 'Y' OR D2.Num = 1)
    ) G (OrderGroup)
;

<强> See it live in a SQL Fiddle

相关问题