仅选择值更改的行

Posted

技术标签:

【中文标题】仅选择值更改的行【英文标题】: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)。相反,您需要使用自联接到前一行。 【参考方案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 会更容易方式

【讨论】:

这实际上似乎比其他示例执行得更快,谢谢!

以上是关于仅选择值更改的行的主要内容,如果未能解决你的问题,请参考以下文章

My SQL with Python:选择具有最高值的行并在那里更改值

UISegmentedControl 值更改时如何仅更改文本颜色?

为啥仅通过更改 SELECT 子句就可以得到零行或具有 NULL 值的行?

如何提取数据框中更改 id-value 的行?

根据数据表行选择更新小部件值

从下拉列表中选择值,第二个下拉列表自动更改