首页 > 解决方案 > T-SQL。更好的是:加入然后组或组然后加入

问题描述

我有 2 张桌子:

命令:

IdProduct (what is ordered - FK to Product table)
Price  (what is the total price for offer)
Piece  (i.e. count - how many products are ordered?) 

产品

Id
Name

并且有 2 个 SQL 语句以每件商品的最优惠价格返回商品:

声明#1:

SELECT 
    p.Name,
    MIN (Price / Piece) AS MinPrice
FROM 
    [ORDER] o
JOIN 
    Product p ON IdProduct = p.Id
GROUP BY
    p.Name

声明#2:

SELECT p.Name, t.MinPrice 
FROM
    (SELECT IdProduct, MIN(Price/Piece) AS MinPrice 
     FROM [Order] 
     GROUP BY IdProduct) t 
JOIN 
    Product p ON p.Id = t.IdProduct

我在 Microsoft SQL Server Management Studio 中调查了执行计划,它们看起来非常相似,但我有几个观察结果:

  1. 为什么第一个计划使用[order by name]指令?即使我不使用 T-SQL Order 指令,它的输出也有按“asc”排序的产品名称

  2. 这种隐含的“按名称排序”会减慢第一个 sql 的速度。当我将“按名称排序”添加到第二个 sql 时 - 它们对于执行计划成本变得相同。

  3. 我猜 sql #2 的性能应该优于 #1,因为:

    一个)。它按PK(即整数)分组,而不是按名称(具有nvarchar列类型,而且没有索引)b)。它仅在第一个分组后才加入表,这应该最大限度地提高性能(与加入完整的 2 个表相比,因为它对第一个 sql 的预期) - 但执行计划仍然显示相同的估计执行成本。

您更喜欢哪种 SQL 语句,为什么?可能您有自己的 SQL 语句版本吗?

标签: sqlsql-servertsql

解决方案


就个人而言,我更喜欢陈述 2。我的理由与你所期望的完全不同。

您是否意识到您的 2 个语句不是为返回相同的结果而构建的?

第一个查询不产品对记录进行分组,而是按产品名称对记录进行分组。在大多数数据库中,被调用name的列从来都不是唯一的。因此,这 2 个GROUP BY是不等价的(也许您的测试数据恰好使 2 个结果相同,但这只是在这里玩的运气)。

这是应该写的:

SELECT 
    p.Name,
    MIN (Price / Piece) AS MinPrice
FROM 
    [ORDER] o
JOIN 
    Product p ON IdProduct = p.Id
GROUP BY
    IdProduct, p.Name /* GROUP BY PK on Product */

恕我直言,第二种语法可以很好地防止这种错误。我建议那是你使用的那个。
当您使用 100 多个表而不是您自己创建和填充的 2 个表处理遗留数据库时,这将为您节省一些麻烦,更不用说第一个语句可能会在很长一段时间内正常工作,直到最终Product.name变得非唯一。

顺便说一句,隐含order by暗示它没有使用 PK 列。它不会减慢您的查询速度。它正在订购记录以准备GROUP BY


PS:要回答您关于性能的问题,您的第二条语句与我所写的语句应该非常相似(感谢查询规划器)。
我有时会看到第一个语句明显慢于第二个语句,但从未明显快于第二个语句(如果存在异常,它们非常罕见,以至于我错过了它们)。

PPS:由于您从 聚合数据,因此在字段中Product添加 a可能会使性能变得更加复杂。 恐怕这是您每次开发新查询时都必须尝试的事情。WHEREOrder


推荐阅读