仅选择值更改的行
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_NUMBER
和LEFT 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 值更改时如何仅更改文本颜色?