【问题标题】:Select only the rows where a value changes仅选择值更改的行
【发布时间】:2021-04-23 23:51:57
【问题描述】:

我正在使用 SQL Server 2008 R2,我正在努力编写一个查询来返回列更改的所有行。

在下表中,我想按日期顺序浏览所有记录,并仅选择金额与该客户之前的金额不同的行。

CustomerId InvoiceId DateInvoice Amount
209 9725772 2020-12-10 9.50
209 9725773 2021-01-15 1.50
209 9725774 2021-01-17 2.50
209 9725775 2021-01-19 3.50
209 9725776 2021-01-21 3.50 *
209 9725777 2021-01-23 9.50
209 9725778 2021-01-25 9.50 *
209 9725779 2021-01-25 3.50
210 9726132 2021-02-02 3.50
210 9726133 2021-03-02 9.50
210 9726134 2021-04-02 9.50 *

我已在金额列中为我不想返回的行添加了一个星号。

如有任何建议,我们将不胜感激。

【问题讨论】:

  • 如果您使用的是受支持的 SQL Server 版本(甚至是扩展支持的版本),这将是微不足道的...LAG (Transact-SQL)/LEAD (Transact-SQL)。相反,您需要使用自联接到前一行。

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


【解决方案1】:

您可以在比您的 SQL Server 版本更高的版本中使用 LAG() 窗口函数,但如果没有它,您可以在 WHERE 子句中使用相关子查询:

SELECT t1.*
FROM tablename t1
WHERE t1.Amount <> COALESCE(
  (
    SELECT TOP 1 t2.Amount
    FROM tablename t2
    WHERE t2.CustomerId = t1.CustomerId AND t2.DateInvoice < t1.DateInvoice
    ORDER BY t2.DateInvoice DESC
  ), -1)

请参阅demo
结果:

CustomerId InvoiceId DateInvoice Amount
209 9725772 2020-12-10 9.50
209 9725773 2021-01-15 1.50
209 9725774 2021-01-17 2.50
209 9725775 2021-01-19 3.50
209 9725777 2021-01-23 9.50
209 9725779 2021-01-25 3.50
210 9726132 2021-02-02 3.50
210 9726133 2021-03-02 9.50

【讨论】:

  • 真的很好,感谢您的帮助,但是有没有更好的方法来处理“-1”位?发票金额可能是-1,我想我可以输入-9999999。
  • @fosbie 您可以将其更改为您认为在“金额”列中永远不会存在的任何数字。我使用 COALESCE() 而不是子查询来缩短代码。
  • @fosbie 在此处检查带有子查询的代码:dbfiddle.uk/…
【解决方案2】:

我刚刚找到了一种方法,但这对我来说看起来很可怕,必须有一种更易读的方法。

SELECT t.CustomerId,
t.InvoiceId,
t.DateInvoice,
t.Amount,
(SELECT TOP 1 Amount 
 FROM #test t1 
 WHERE t1.CustomerId=t.CustomerId AND t1.DateInvoice<t.DateInvoice 
 ORDER BY DateInvoice DESC) AS PrevAmount
 FROM #test AS t
 WHERE ((SELECT TOP 1 Amount 
    FROM #test t1
    WHERE t1.CustomerId=t.CustomerId AND t1.DateInvoice<t.DateInvoice 
    ORDER BY DateInvoice DESC)) <> Amount 
 Or ((SELECT TOP 1 Amount 
 FROM #test t1 
 WHERE t1.CustomerId=t.CustomerId AND t1.DateInvoice<t.DateInvoice 
 ORDER BY DateInvoice DESC)) Is Null

【讨论】:

    【解决方案3】:

    可以在前一行使用带有ROW_NUMBERLEFT JOIN 的CTE:

    WITH CTE AS(
        SELECT CustomerId,
               InvoiceId,
               DateInvoice,
               Amount,
               ROW_NUMBER () OVER (PARTITION BY CustomerID ORDER BY DateInvoice ASC) AS RN
        FROM dbo.YourTable)
    SELECT C1.CustomerId,
           C1.InvoiceId,
           C1.DateInvoice,
           C1.Amount
    FROM CTE C1
         LEFT JOIN CTE C2 ON C1.CustomerId = C2.CustomerId
                         AND C1.Amount = C2.Amount
                         AND C1.RN = C2.RN + 1
    WHERE C2.CustomerId IS NULL;
    

    使用forpas的样本数据:db<>fiddle

    但是LAG/LEAD 会更容易方式

    【讨论】:

    • 这实际上似乎比其他示例执行得更快,谢谢!
    猜你喜欢
    • 2020-12-20
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    • 1970-01-01
    相关资源
    最近更新 更多