尝试使用T-SQL中的暂态状态编号

时间:2017-12-08 19:03:25

标签: sql-server tsql

我有一个看起来像这样的表(没有尝试列):

Account      Status        Date         Attempt(*)
A            In_PROGRESS   12/12/2017   1
A            ERROR         13/12/2017   1
A            SUCCESS       15/12/2017   2
B            ERROR         10/12/2017   1
B            SUCCESS       13/12/2017   2
C            IN_PROGRESS   11/12/2017   1
C            ERROR         13/12/2017   1
C            ERROR         14/12/2017   2
C            IN_PROGRESS   15/12/2017   3

我想获得Attempt(*)列。 ERROR和SUCCESS状态是最终状态。例如,对于帐户C,我们有第一次尝试产生ERROR,第二次尝试产生ERROR,第三次尝试产生IN_PROGRESS。

我知道RANK()因为短暂的状态而无法工作,并且不能正常工作。例如,如果我要去:

RANK()OVER(按帐户ORDER按日期ASC划分)

我会得到:

Account      Status        Date         Attempt(*)
A            In_PROGRESS   12/12/2017   1
A            ERROR         13/12/2017   2
A            SUCCESS       15/12/2017   3
B            ERROR         10/12/2017   1
B            SUCCESS       13/12/2017   2
C            IN_PROGRESS   11/12/2017   1
C            ERROR         13/12/2017   2
C            ERROR         14/12/2017   3
C            IN_PROGRESS   15/12/2017   4

不考虑暂时状态。我会使用第二个表中的结果创建一个新列来获得所需的结果,sudo代码将类似于:

New_Attempt [n] =如果尝试(*)= 1则为1其他(如果尝试(*)> 1且状态[n-1]在(错误,成功)中则尝试[n-1] + 1其他尝试[N-1])

其中' n'是行号。

我对SQL比较陌生,我研究过其他排名功能,比如NTILE和DENSE_RANK,但我仍然无法找到一种方法。

P.S。我在SQL Server Management Studio 2008中使用T-SQL

谢谢! :)

2 个答案:

答案 0 :(得分:3)

对于每一行,计算同一帐户中不是IN_PROGRESS的所有先前行,并将1添加到结果中。

C帐户为例:

Account  Status       Date        Attempt (expected values)
-------  -----------  ----------  -------
C        IN_PROGRESS  11/12/2017  1
C        ERROR        13/12/2017  1
C        ERROR        14/12/2017  2
C        IN_PROGRESS  15/12/2017  3

显然,第一行之前没有行,这意味着也有0个非IN_PROGRESS行。拿下并添加1,你将得到1作为Attempt的值。第二行也是如此:在它之前仍然没有非IN_PROGRESS行,因此Attempt也将为1。

现在对于第三行,您之前已经有一行ERROR行,因此计数将为1,最后为Attempt值,因此为2。

对于最后一行,它前面有两个非IN_PROGRESS行(两个ERROR行),因此计数为2,最终结果为3.

您可以看到上述说明中的所有值都与您指定的预期值相匹配。现在,如果在窗口聚合函数中使用ORDER BY(换句话说,如果您使用的是SQL Server 2012或更高版本),则可以实现逻辑:

SELECT
  Account,
  Status,
  Date,
  Attempt = 1 + COUNT(CASE WHEN Status <> 'IN_PROGRESS' THEN 1 END)
                OVER (PARTITION BY Account
                      ORDER BY Date
                      ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
FROM
  dbo.YourTable
;

这就是您可以在SQL Server 2008中使用的方法来解决缺少的功能:

SELECT
  Account,
  Status,
  Date,
  Attempt = 1 + (SELECT
                   COUNT(*)
                 FROM
                   dbo.YourTable AS sub
                 WHERE
                   sub.Account = main.Account
                   AND sub.Date < main.Date
                   AND sub.Status <> 'IN_PROGRESS')
FROM
  dbo.YourTable AS main
;

注意:假设Date唯一标识帐户中的行。

答案 1 :(得分:1)

这不是世界上最漂亮的查询,但它会为您提供我认为您想要的结果。基本上我只是按照IN_PROGRESS的规则结束了尝试的计数。我在示例集中添加了一些记录来证明功能。我还想指出,这种解决方案可能无法很好地扩展。因此,如果您的数据集很大,请注意。

CREATE TABLE #Test (Account char(1), [Status] varchar(50), [Date] datetime)

INSERT INTO #Test (Account, [Status], [Date])VALUES
('A',   'IN_PROGRESS',   '12/12/2017'),
('A',   'ERROR',         '12/13/2017'),
('A',   'SUCCESS',       '12/15/2017'),
('A',   'IN_PROGRESS',   '12/16/2017'),
('A',   'IN_PROGRESS',   '12/17/2017'),
('B',   'ERROR',         '12/10/2017'),
('B',   'SUCCESS',       '12/13/2017'),
('B',   'IN_PROGRESS',   '12/15/2017'),
('B',   'IN_PROGRESS',   '12/17/2017'),
('C',   'IN_PROGRESS',   '12/11/2017'),
('C',   'ERROR',         '12/13/2017'),
('C',   'ERROR',         '12/14/2017'),
('C',   'IN_PROGRESS',   '12/15/2017'),
('C',   'ERROR',         '12/16/2017'),
('C',   'ERROR',         '12/17/2017')

SELECT 
    T1.Account, 
    T1.[Status], 
    T1.[Date], 
    1 + SUM(COALESCE(TC.Attempt, 0)) AS 'Attempt(*)'
FROM 
    #Test AS T1
    OUTER APPLY
    (
        SELECT
            1 AS 'Attempt'
        FROM
            #Test AS T2
        WHERE
            T2.Account = T1.Account
            AND T2.[Date] < T1.[Date]
            AND T2.[Date] > COALESCE((SELECT TOP 1 T3.[Date] FROM #Test AS T3 WHERE T3.Account = T1.Account AND T3.[Status] = 'IN_PROGRESS' AND T3.[Date] < T1.[Date] ORDER BY T3.[Date] DESC), '01/01/2017')
            AND T2.Status <> 'IN_PROGRESS'
    ) AS TC
GROUP BY
    T1.Account, 
    T1.[Status], 
    T1.[Date]
ORDER BY
    T1.Account,
    T1.[Date]

DROP TABLE #Test