我试图获得表中Price
产品的最小值。Description
ProductVariation
以这种方式创建查询时:
SELECT productId, MIN([price]) AS price
FROM ProductVariation
GROUP BY ProductId
Run Code Online (Sandbox Code Playgroud)
它按预期工作,分组依据ProductId
和最小值Price
:
产品编号 | 价格 |
---|---|
3 | 299.99 |
6 | 299.99 |
7 | 299.99 |
8 | 199.99 |
9 | 299.99 |
10 | 299.99 |
11 | 159.99 |
12 | 159.99 |
13 | 189.99 |
14 | 189.99 |
但是,当我将Description
列添加到SELECT
:
SELECT productId, [Description], MIN([price]) AS price
FROM ProductVariation
GROUP BY ProductId, [Description]
Run Code Online (Sandbox Code Playgroud)
它显示所有行,重复ProductId
列。
产品编号 | 描述 | 价格 |
---|---|---|
3 | 自然的 | 299.99 |
6 | 饼干和奶油 | 299.99 |
7 | 莫兰戈 | 299.99 |
8 | 包尼利亚 | 199.99 |
8 | 巧克力 | 299.99 |
9 | 卡拉梅洛 | 299.99 |
10 | 包尼利亚 | 299.99 |
10 | 饼干和奶油 | 399.99 |
10 | 莫兰戈 | 399.99 |
11 | 包尼利亚 | 199.99 |
11 | 莫兰戈 | 159.99 |
我该如何修复它以显示独特的 ProductId
值以及Description
像这样的东西?
产品编号 | 描述 | 价格 |
---|---|---|
3 | 自然的 | 299.99 |
6 | 饼干和奶油 | 299.99 |
7 | 莫兰戈 | 299.99 |
8 | 包尼利亚 | 199.99 |
9 | 卡拉梅洛 | 299.99 |
10 | 包尼利亚 | 299.99 |
11 | 莫兰戈 | 159.99 |
12 | 包尼利亚 | 159.99 |
13 | 卡拉梅洛 | 189.99 |
14 | 莫兰戈 | 189.99 |
如果你能解释一下为什么它不按我的方式工作?
您可以使用窗口函数,例如ROW_NUMBER()
枚举 的分组(分区)中的行ProductId
,按Price
升序排序,然后SELECT
仅查找最小值Price
行。
例如:
WITH _ProductVariationsSorted AS
(
SELECT
ProductId,
Description,
Price,
ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY Price) AS SortId -- Generate a unique ID within each group of ProductId rows, sorted by the Price
FROM dbo.ProductVariation
)
SELECT
ProductId,
Description,
Price
FROM _ProductVariationsSorted
WHERE SortId = 1; -- Only return the rows with the lowest Price per ProductId grouping
Run Code Online (Sandbox Code Playgroud)
另一种方法(感谢 Erik Darling 提醒我)就是直接对MIN()
聚合函数进行窗口化。您还需要DISTINCT
行。您可能会发现这简化了代码,如下所示:
SELECT DISTINCT
ProductId,
Description,
MIN(Price) OVER (PARTITION BY ProductId) AS Price
FROM dbo.ProductVariation
Run Code Online (Sandbox Code Playgroud)
最后,第三种但类似的方法也是使用FIRST_VALUE()
窗口函数:
SELECT DISTINCT
ProductId,
Description,
FIRST_VALUE(Price) OVER (PARTITION BY ID ORDER BY PRICE) AS Price
FROM dbo.ProductVariation
Run Code Online (Sandbox Code Playgroud)
尽管根据您的数据大小以及表和索引的架构方式,您可能会发现其中一种解决方案比另一种解决方案性能更高。始终进行相应的测试。
另一件需要注意的重要事情是,在使用窗口函数时,是否存在联系您要窗口化的列(在同一分组内)中的值
当按绑定值排序时,该ROW_NUMBER()
函数将不确定地随机选择具有绑定的行之一来枚举行。然后,在过滤 时,其中一个联系将被进一步过滤掉WHERE SortId = 1
。DISTINCT
使用和 或 的SUM() OVER
解决方案FIRST_VALUE()
将包括两个相连的行。
例如,在您的数据中,看起来有ProductId = 10
两行具有相同的Price = 399.99
,Cookies & Cream
和Morango
。现在,这实际上并不会在您的数据中造成问题,因为还有另一行具有ProductId = 10
较低的Price
.
但是,让我们假设有一天变体不再销售并且该行被删除。现在你的最低Price
要求ProductId = 10
是399.99
两个变体之间的平局。使用该ROW_NUMBER()
解决方案,每次运行查询时,您只会随机返回其中一行。使用SUM() OVER
或FIRST_VALUE()
解决方案,您将恢复两行。
根据您的目标,这些结果可能都不是您想要的。即,如果您确实只想一次仅显示 1 个变体,即使在Price
同一个变量中存在关联ProductId
,并且您希望每次运行查询时它始终可靠地为相同的变体。
要实现此目的,您必须向窗口函数ORDER BY
的子句添加一个确定性的仲裁表达式ROW_NUMBER()
,例如,像列这样的唯一列Id
。代码将如下所示ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY Price, Id) AS SortId
:现在返回哪一行的规则是具有最低 的行Price
,并且在最低的并列中Price
,然后是并列中具有最低的行Id
(理论上首先输入到ProductVariations
表中)。