2009-07-08 59 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 - 平均執行時間大致相等。

它的價值卻指出,這不會總是這種情況(特別是評估一個子查詢內部函數時),以及有時你可能會碰到由於子查詢的問題。但一般來說,只有在遇到性能問題時才最好擔心這種情況。

相關問題