2009-07-08 26 views
6

我更喜欢在t-sql中使用有效的内联方式进行编码,而不是在存储过程或视图末尾有一长串联接。编码内连接的两种方法中的哪一种更快?

例如,我的代码:

SELECT  PKey , Billable, 
    (SELECT LastName FROM Contact.dbo.Contacts WHERE (Pkey = Contacts_PKey)), 
    (SELECT Description FROM Common.dbo.LMain WHERE (PKey= DType)), 
    (SELECT TaskName FROM Common.dbo.LTask WHERE (PKey = TaskType)) , 
    StartTime, EndTime, SavedTime 
FROM dbo.TopicLog where StartTime > '7/9/09' ORDER BY StartTime 

不是

SELECT t.PKey, t.Billable, c.LastName, m.Description, lt.TaskName, t.StartTime, t.EndTime, t.SavedTime 
FROM dbo.TopicLog AS t  
inner join Contact.dbo.Contacts as c on c.Pkey = t.Contacts_PKey and t.StartTime > '7/9/09' 
inner join Common.dbo.LMain as m on m.PKey = t.DType 
inner join Common.dbo.LTask as lt on lt.PKey = t.TaskType 
ORDER BY t.StartTime 

我喜欢这种类型的语法,因为它是如此混乱少得多写或调试时,尤其是当有很多表被加入或其他东西正在进行(case语句,t-sql函数,自加入等)

但我的问题是 - 我正在通过查询数据库就这样。

我还没有收集足够的数据来测量差异,但我会在某个时间点上路。

我想在进一步研究之前找出答案。我不想以后再回去重新编码一切以提高性能。

回答

20

第二个(实际的内部连接),一般。第一个(子查询)对每一行都进行3次查询,但这通常由编译器进行管理,以便减少差异。

还没有:Check the query execution plans自己!

由于性能下降,我的猜测是您的表格没有正确编制索引。您应该在所有主键上都有聚簇索引,并且在外键上有非聚簇索引(用于创建联接的索引)。

我要指出,这两个查询是等价的,当且仅当你在所有的连接条件都有匹配的值(即,始终返回所有行从主表)。否则,如果没有匹配,您将从子查询中获得null。内部连接主动过滤掉任何不符合连接条件的行。子查询方法实际上等同于(在结果中,不是速度或执行)到左外部联接。

+1

+1。正如你指出的那样,通过深思熟虑的索引获得的胜利更有可能产生显着的收益。 (但检查执行计划会让他们知道!) – Beska 2009-07-08 20:07:59

+2

+1“检查自己的查询执行计划!”这是确定的唯一方法。优化器*可能会自动将它们变成JOIN。虽然,这两个查询不完全相同。 #1是一个LEFT JOIN,#2是一个INNER JOIN。所以他们会给你不同的计划。 – beach 2009-07-08 20:10:11

+0

这很容易让人误解 - 它是一个常见的误解,即由于您给出的原因,子查询比较慢,实际上SQL服务器在重新编译过程中尽可能将子查询重写为连接。 – Justin 2009-07-13 10:16:17

0

一般来说子查询(即第一个例子)是慢,但最简单的方法来优化和分析你的查询是通过特定的数据库尝试它们,MS SQL服务器提供了出色的分析和性能调整工具。

+0

这简直是不真实的 - 通常SQL服务器将子查询解析为与连接生成的执行树相同的执行树。 – Justin 2009-07-13 10:29:08

10

第一种方法根本不是内连接,它是一个相关的子查询。 它们更像是左外连接而不是内连接,因为它们将在没有匹配值时返回NULL。

3

第一个看起来像一个病态的方式来加入我。我会避免它,如果没有其他原因,它是不寻常的 - 有经验的SQL DBA看着它,保持它会花一段时间搜索的原因,为什么它的编码一样的是,在没有真正的理由,只要你想要查询。如果缺少数据,它的行为更像是一个外连接。

第二个例子看起来很正常。

你应该知道,在做内部的老派的方式加入是这样的:

SELECT t.PKey, t.Billable, 
c.LastName, m.Description, lt.TaskName, 
t.StartTime, t.EndTime, t.SavedTime 
FROM 
dbo.TopicLog as t, Contact.dbo.Contacts as c, 
Common.dbo.LMain as m, Common.dbo.LTask as lt 
WHERE c.Pkey = t.Contacts_PKey and t.StartTime > '7/9/09' 
    AND m.PKey = t.DType 
    AND lt.PKey = t.TaskType 
ORDER BY t.StartTime 

,并在猜测这相当于现代的“内上加入”语法一旦它被解析。

正如另一个答案所说,如果您正在寻找更快的查询,首先要做的是检查表的索引是否已经排除。然后查看查询执行计划。

+0

看起来这个语法是他所追求的。对于每个从中选择的行表执行子查询的索引或没有索引即使是一个小表(例如多于4000行)也会很慢。 – Jon 2009-07-08 20:19:05

0

许多SQL程序员完全不知道优化器经常将子查询解析为连接。任何一个查询都可能没有性能问题。

查看执行计划!

1

在OP这两个查询说完全不同的东西,只产生相同的结果,如果正确的数据模型假设到位:

  1. 每个在查找中使用的列有NOT NULL约束和外键约束。

  2. 使用查找表的主键或唯一键。

可能在OP特定情况下,这些假设是正确的,但在一般情况下,这是不同的。

正如其他人指出的那样,子查询更像是一个外部联接,因为它将为列LastName,Description和Taskname返回一个空值,而不是完全过滤掉该行。

此外,如果其中一个子查询返回多个行,则会出现错误。

就个人喜好而言,我更喜欢第二个例子加入连接语法,但这是主观的。

0

我认为第二个执行速度更快。 这个背后的原因是通过使用别名(例如t,c,m等)名称关系引擎可以轻松找出指向表格位置的指针。

我认为这是sql tunning中的技巧之一。

1

一般来说有没有区别简单的子查询的性能VS连接 - 这是一个误解,认为子查询慢得多(因为SQL服务器必须通过内部查询环路),但是一般来说这是根本不真实!在编译过程中,SQL服务器会生成一个执行树,并且通常在这些树中,子查询等同于连接。

其值得注意的是,你的两个查询逻辑上不相同,产生不同的结果对我来说,第二个查询实际上应沿着线的东西:(这仍然心不是完全相同,但其更接近)

SELECT t.PKey, t.Billable, c.LastName, m.Description, lt.TaskName, t.StartTime, t.EndTime, t.SavedTime 
FROM dbo.TopicLog AS t  
LEFT OUTER JOIN Contact.dbo.Contacts as c on c.Pkey = t.Contacts_PKey 
LEFT OUTER JOIN Common.dbo.LMain as m on m.PKey = t.DType 
LEFT OUTER JOIN Common.dbo.LTask as lt on lt.PKey = t.TaskType 
WHERE t.StartTime > '7/9/09' 
ORDER BY t.StartTime 

在我的测试中,子查询产生了一个执行计划,读取次数极少(15次而不是1000次),然而稍微高一点的cpu - 平均执行时间大致相等。

它的价值却指出,这不会总是这种情况(特别是评估一个子查询内部函数时),以及有时你可能会碰到由于子查询的问题。但一般来说,只有在遇到性能问题时才最好担心这种情况。