从this article开始,作者决定使用WITH
替换以这种方式使用的子查询:
SELECT c.CategoryName, p.ProductName, p.UnitPrice
FROM Categories c
INNER JOIN (SELECT CategoryId, MAX(UnitPrice) AS MaxPrice
FROM Products GROUP BY CategoryId) maxprice
ON maxprice.CategoryId = c.CategoryId
INNER JOIN Products p
ON p.CategoryId = c.CategoryId
AND p.UnitPrice = maxprice.MaxPrice
ORDER BY MaxPrice DESC
以下是作者的查询结果:
WITH MostExpensiveProducts (CategoryId, MaxUnitPrice) AS
(
SELECT CategoryId, MAX(UnitPrice)
FROM Products
GROUP BY CategoryId
)
SELECT c.CategoryName, p.ProductName, p.UnitPrice
FROM Categories c
INNER JOIN MostExpensiveProducts mep
ON mep.CategoryId = c.CategoryId
INNER JOIN Products p
ON p.CategoryId = mep.CategoryId
AND p.UnitPrice = mep.MaxUnitPrice
ORDER BY mep.MaxUnitPrice DESC;
现在这篇文章已经超过6年,但它仍然与我相关,因为我使用的是SQL Server 2008.但是,我的大部分搜索结果都围绕替换子查询而涉及JOINS
。这对我来说很困惑,因为这里的子查询已经在INNER JOINs
。
使用WITH
这个解决方案今天仍然有效吗?有没有更好的方法来解决这个问题?
答案 0 :(得分:2)
序言:
话虽如此,我发现这个例子非常有线:它列出了每个类别中最昂贵的产品(如果一个类别中的产品价格相同,可能会超过一个)。
话虽如此,仍然有一个不再需要的连接:Products
表与WITH
子句的结果的连接(不是p.UnitPrice = mep.MaxUnitPrice
上的连接。我看到了,这通常是一个错误。我现在就会假设。
如果打算每个类别显示最昂贵的产品(只有一个关系),@ Tab Alleman在我写作时发布的解决方案是可以的。如果您确实需要列出多行,如果有关系,您可以在@Tab Alleman的解决方案中使用RANK()
(或DENSE_RANK()
代替ROW_NUMBER()
。
使用更现代的SQL来解决此要求的另一种方法是使用LATERAL
:这允许您应用每个类别TOP
(或LIMIT
或FETCH FIRST ... ROWS ONLY
子句)。在SQL Server中,实际上称为CROSS APPLY
(LATERAL
是SQL Standard和其他数据库使用的关键字。)
SELECT c.CategoryName, p.ProductName, p.UnitPrice
FROM Categories c
CROSS APPLY (SELECT TOP 1 ProductName, UnitPrice
FROM Products
WHERE Products.Category = c.CategoryId
ORDER BY UnitPrice DESC) p
(我没有运行此查询,错误是无意的;)如果是关系,则没有指定两个同等价格产品中的哪一个出现。
OVER
解决方案和LATERAL
/ APPLY
解决方案的效果可能会有所不同(最好取决于您拥有的数据和索引)。
LATERAL
/ APPLY
解决方案还可以在出现问题时提供所有产品。 SQL标准允许FETCH FIRST ... ROWS ONLY
具有WITH TIES
修饰符。显然,SQL Server也有TOP 1 WITH TIES
(source)。
如果您想了解更多有关您可能还不知道的有用SQL功能的信息,请查看以下幻灯片:https://modern-sql.com/slides
答案 1 :(得分:1)
此解决方案确实使用CTE,但它使用ROW_NUMBER()而不是子查询来获取每个类别中最昂贵的产品,因此避免了两次加入产品:
WITH cte AS (
SELECT CategoryId, ProductId, UnitPrice,
ROW_NUMBER() OVER (PARTITION BY CategoryId ORDER BY UnitPrice DESC) rn
FROM Products
)
SELECT CategoryId, ProductId, UnitPrice
FROM cte
WHERE rn=1
ORDER BY UnitPrice DESC