【问题标题】:Attempt number over transitory statuses in T-SQLT-SQL 中临时状态的尝试次数
【发布时间】:2017-12-08 19:03:25
【问题描述】:

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

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。

我知道由于暂时状态“IN_PROGRESS”,单独的 RANK() 不起作用。例如,如果我要去:

RANK() OVER(PARTITION BY Account ORDER BY Date 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] = if Attempt(*) = 1 then 1 else (if Attempt(*) > 1 and Status[n-1] in(ERROR, SUCCESS) then Attempt[n-1] + 1 else Attempt [n-1])

其中 'n' 是行号。

我对 SQL 比较陌生,我研究过 NTILE 和 DENSE_RANK 等其他排名函数,但我仍然想不出办法。

附:我在 SQL Server Management Studio 2008 中使用 T-SQL

谢谢! :)

【问题讨论】:

  • 您在这里确定“尝试”的逻辑是什么。仅看您的示例还不清楚。是“只要记录不是“In_PROGRESS”,我们就会添加到Accounts 尝试中,在这种情况下Attempt 不会增加”?
  • "*除非Account 的最新记录是In_PROGRESS,否则将其视为一次尝试和增量"?
  • 您好 JNevill,对于不明确的解释深表歉意。是的,没错。因此,可以在存在 ERROR 或 SUCCESS 状态的点定义尝试的结束。在这些之前的任何事情都是下一次尝试。这有帮助吗?
  • 请阅读this,了解一些改进问题的技巧。
  • 与其添加线索作为 cmets,不如编辑您的问题以提供完整的解释。

标签: sql-server tsql


【解决方案1】:

对于每一行,计算同一帐户之前所有不是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 唯一标识帐户中的一行。

【讨论】:

    【解决方案2】:

    这不是世界上最漂亮的查询,但它会给你我认为你想要的结果。基本上我只是遵循 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
    

    【讨论】:

    • 更新 - 抱歉,这不是我要找的答案,尝试的第 9 个条目应该是 3。
    • 所以你说这个条目应该是3? "B IN_PROGRESS 2017-12-17 1"
    猜你喜欢
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    • 2018-01-08
    • 1970-01-01
    相关资源
    最近更新 更多