【问题标题】:Group records with incrementing count based on flag基于标志递增计数的分组记录
【发布时间】:2014-04-11 02:58:40
【问题描述】:

两个...

实际数据:

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 组合都被视为一个“组”,按日期排序,并应分配一个 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

【问题讨论】:

  • 您的问题到底是什么?你试过什么?请分享这些额外信息。
  • 不确定我是如何完全忽略我的问题的。更新了我原来的问题。感谢您指出。
  • 你可以为此准备 sqlfiddle 吗?
  • 为示例数据添加了提琴手链接。

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


【解决方案1】:

所以这里有一个方法来做到这一点。它基于How do I calculate a running total in SQL without using a cursor?,确实存在一些缺陷。我在建议上使用索引,它使排序工作尽管事实上不能保证更新的顺序。

还值得将您指向 Calculate running total / running balance 进行 Aaron Bertrand 治疗。

这里可能聪明的一点是将 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

【讨论】:

  • 这似乎有效。我将进行一些更大的测试,但在此先感谢。我不知道为什么我试图通过使用 CTE 来过度复杂化它,而我本可以像你一样使用几个临时表来完成它。
  • 有递归的CTEs 和非递归的。后者可以执行临时表的角色,该表在查询的生命周期中存在并且不再存在。
  • CTE 通常不会过于复杂。我可以索引一个 CTE,所以它不是一个完美的替代品。除此之外,我想我可能有 UPDATE..OUTPUT 一个 CTE 以获得相同的结果。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    相关资源
    最近更新 更多