将列添加到 SELECT 后,MIN 函数返回多行

gre*_*ryp 3 t-sql

我试图获得表中Price产品的最小值。DescriptionProductVariation

在此输入图像描述

以这种方式创建查询时:

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

如果你能解释一下为什么它不按我的方式工作?

J.D*_*.D. 5

您可以使用窗口函数,例如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 = 1DISTINCT使用和 或 的SUM() OVER解决方案FIRST_VALUE()将包括两个相连的行。

例如,在您的数据中,看起来有ProductId = 10两行具有相同的Price = 399.99,Cookies & CreamMorango。现在,这实际上并不会在您的数据中造成问题,因为还有另一行具有ProductId = 10较低的Price.

但是,让我们假设有一天变体不再销售并且该行被删除。现在你的最低Price要求ProductId = 10399.99两个变体之间的平局。使用该ROW_NUMBER()解决方案,每次运行查询时,您只会随机返回其中一行。使用SUM() OVERFIRST_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表中)。