哪个更快:使用GROUP BY或子查询加入?

时间:2010-07-01 08:23:24

标签: sql-server sql-server-2008 join group-by subquery

假设我们有两个表:'Car'和'Part',在'Car_Part'中有一个连接表。说我想看到所有车辆中都有123部分。我能做到这一点:

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
INNER JOIN Car_Part ON Car_Part.Car_Id = Car.Car_Id
WHERE Car_Part.Part_Id = @part_to_look_for
GROUP BY Car.Col1, Car.Col2, Car.Col3

或者我可以这样做

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
WHERE Car.Car_Id IN (SELECT Car_Id FROM Car_Part WHERE Part_Id = @part_to_look_for)

现在,我内心的一切都想要使用第一种方法,因为我是由善良的父母抚养长大的,他们向我灌输了对次级查询的纯粹仇恨和对集合理论的热爱,但有人向我建议做那个大的GROUP BY比一个子查询更糟糕。

我应该指出我们在SQL Server 2008上。我还应该说实际上我想根据部件ID,部件类型以及可能的其他东西进行选择。所以,我想要做的查询实际上是这样的:

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
INNER JOIN Car_Part ON Car_Part.Car_Id = Car.Car_Id
INNER JOIN Part ON Part.Part_Id = Car_Part.Part_Id
WHERE (@part_Id IS NULL OR Car_Part.Part_Id = @part_Id)
AND (@part_type IS NULL OR Part.Part_Type = @part_type)
GROUP BY Car.Col1, Car.Col2, Car.Col3

或者...

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
WHERE (@part_Id IS NULL OR Car.Car_Id IN (
    SELECT Car_Id 
    FROM Car_Part 
    WHERE Part_Id = @part_Id))
AND (@part_type IS NULL OR Car.Car_Id IN (
    SELECT Car_Id
    FROM Car_Part
    INNER JOIN Part ON Part.Part_Id = Car_Part.Part_Id
    WHERE Part.Part_Type = @part_type))

3 个答案:

答案 0 :(得分:4)

您可以做的最好的事情就是在实际数据量上自己测试它们。这不仅有利于这个查询,而且对于所有未来的查询,当您不确定哪种是最佳方式时。

要做的重要事情包括:
- 测试生产水平数据量
- 公平地测试&始终如一(清除缓存:http://www.adathedev.co.uk/2010/02/would-you-like-sql-cache-with-that.html
- 检查执行计划

您可以使用SQL事件探查器监视并检查持续时间/读/写/ CPU,或SET STATISTICS IO ON; SET STATISTICS TIME ON;以在SSMS中输出统计信息。然后比较每个查询的统计数据。

如果你不能进行这种类型的测试,那么你可能会面临性能问题,然而你必须调整/纠正这些问题。您可以使用可以为您生成数据的工具。

答案 1 :(得分:3)

我有类似的数据,所以我检查了两种查询样式的执行计划。令我惊讶的是,子查询中的列(CIS)生成了一个执行计划,其I / O成本比内部联接(IJ)查询少25%。在CIS执行计划中,我获得了中间表(Car_Part)的2个索引扫描与中间的索引扫描以及IJ中相对更昂贵的散列连接。我的索引是健康的但非聚集的,因此可以通过聚类来使索引扫描更快一些。我怀疑这会影响散列连接的成本,这是IJ查询中更昂贵的一步。

与其他人指出的一样,这取决于您的数据。如果您在这3张表中使用了许多千兆字节,那么请调整一下。 如果您的行编号为数百或数千,那么您可能会在非常小的性能增益上分裂头发。我会说IJ查询更具可读性,只要它足够好,就行了任何未来的开发人员都会为您的代码提供帮助,并为他们提供更易于阅读的内容。我的表中的行数是188877,283912,13054,并且在更短的时间内返回两个查询以便啜饮咖啡。

小后记:由于你没有聚合任何数值,看起来你的意思是选择不同的。除非你真的要对小组做些什么,否则更容易看到你的意图选择distinct而不是group by。 IO成本是相同的,但一个表明你的意图更好恕我直言。

答案 2 :(得分:2)

使用SQL Server 2008,我希望In更快,因为它等同于此。

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
WHERE EXISTS(SELECT * FROM Car_Part
            WHERE Car_Part.Car_Id = Car.Car_Id
            AND Car_Part.Part_Id = @part_to_look_for
)

即。它只需要检查行的存在不加入它然后删除重复。这是discussed here