SQL子查询还是INNER-JOIN?

时间:2009-11-24 08:32:18

标签: sql-server performance sql

我有以下两个问题:

declare @UserId as int
set @UserId = 1

-- Query #1: Sub-query
SELECT
    u.[Id] ,
    u.[Name] ,
    u.[OrgId] AS Organization,
    (SELECT o.[Name] FROM Org o WHERE o.Id = u.OrgId) As OrganizationName,
    [UserRoleId] AS UserRole,
    [UserCode] AS UserCode,
    [EmailAddress] As EmailAddress, 
    (SELECT SearchExpression FROM SearchCriteria WHERE UserId = @UserId AND IsDefault=1 ) AS SearchCriteria,
    (SELECT PageSize FROM UserPreferences WHERE UserId = @UserId) AS UserPreferencePageSize,
    (SELECT DrilldownPageSize FROM UserPreferences WHERE UserId = @UserId) AS UserPreferenceDrilldownPageSize
    FROM [User] as u
WHERE u.Id = @UserId

-- Query #2: LEFT OUTER JOIN-query
SELECT
    u.[Id] ,
    u.[Name] ,
    u.[OrgId] AS Organization,
    (SELECT o.[Name] FROM Org o WHERE o.Id = u.OrgId) As OrganizationName,
    [UserRoleId] AS UserRole,
    [UserCode] AS UserCode,
    [EmailAddress] As EmailAddress, 
    sc.SearchExpression As SearchExpression,
    up.PageSize As PageSize,
    up.DrilldownPageSize As DrilldownPageSize    
    FROM [User] as u
LEFT OUTER JOIN [UserPreferences] as up ON u.id = up.UserId
LEFT OUTER JOIN [SearchCriteria] as sc ON u.id = sc.UserId
    WHERE ISNULL(sc.IsDefault,1)=1 AND u.Id = @UserId

查询执行计划统计信息:(相对于批次的查询成本)

  • 查询#1(子查询):56%
  • 查询#2(加入):44%

我thot子查询将是最佳的,因为子查询将在应用WHERE过滤器后执行。统计数据表明查询#2 - JOIN方法更好。

请建议。同样作为一个温和的SQL-Server用户,我如何能够推导出更好的查询(除了执行计划之外的任何其他内容,如果更有帮助的话)

谢谢。

4 个答案:

答案 0 :(得分:12)

join比子查询更快。

子查询可以实现繁忙的磁盘访问,可以考虑硬盘的读写指针(head?),它在访问时来回传递:User,SearchExpression,PageSize,DrilldownPageSize,User,SearchExpression, PageSize,DrilldownPageSize,User ...等等。

join通过集中操作对前两个表的结果进行工作,任何后续连接都会集中连接到第一个连接表的内存(或缓存到磁盘)结果,并且等等。更少的读写针运动,因此更快

答案 1 :(得分:4)

你能做的最好的事情就是尝试两者并比较一下能给你带来最佳表现的东西。很难再次猜测查询优化器会做什么(您可以编写2个不同的查询,实际上最终会针对同一个执行计划进行优化)。

为了公平地比较性能,您应该确保通过在尝试每个执行计划和数据缓存之前清除执行计划和数据缓存从级别游戏领域尝试它们。这可以使用以下命令完成,但只能在开发/测试数据库服务器上执行此操作:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

我通常采用的方法是运行每个查询3次,运行SQL事件探查器,这样我就可以监视查询的持续时间,读取,CPU和写入,然后根据我的决定进行查询。

e.g。
1)使用上述命令清除缓存
2)运行查询和记录统计数据 3)清除缓存
4)再次运行查询
5)再次运行查询(这将使用缓存的执行计划/数据)

然后重复第二个查询进行比较。

答案 2 :(得分:1)

这在很大程度上取决于数据的基数:如果与join大量数据的开销相比,您的内联查找最小(当您只需从该连接中提取一小部分时)结果),那么内联选项会更快。但是如果你在线内选择有很大的开销(即如果你的结果有很多行,并且你正在为每一行调用一个内联选择),那么连接会更快。

我无法从你的问题中看到所涉及的数字(即多少行),因此很难做出定性评论。

例如,如果结果集有10行,则仅对这10行中的每一行执行内联选择,而连接可能涉及更多行,然后由WHERE子句选择性地减少。但是如果你有一个1000万行的结果集,那么内联选择很可能会破坏性能,因为它是逐行的。

示例:想象一下你必须从整个建筑场地收集一堆砖(由大小等指定)并将它们涂成蓝色。

内联选择 =选择所需的所有砖块,然后手工绘制。

加入 =将所有砖块倒入一大桶油漆中,然后选择你需要的砖块

如果你只想要10块砖,那么选择然后手工绘制要快得多。如果你想要一百万块砖,那么首先在浴缸中进行大规模涂漆是可行的方法。

答案 3 :(得分:1)

执行计划的相对成本并不总是可靠的绩效指标。

我假设从你的sql中只返回1行。假设UserId是User上的唯一键,那么在大多数关系数据库中,您的2种方法的性能将类似。

要记住的事情是:

  • 如果UserPreferences或SearchCriteria返回超过1行,第一种方法会引发sql错误,第二种方法将返回超过1行。
  • 第一种方法中明显的额外查找(UserPreferences选择两次)没有实际效果,因为对于第二次查找,记录已经在缓冲区中
  • 如果由于某种原因User表被扫描表空间,第一种方法会更快