2008-10-23 141 views
21

我有以下查詢:按照什麼順序評估MySQL JOIN?

SELECT c.* 
FROM companies AS c 
JOIN users AS u USING(companyid) 
JOIN jobs AS j USING(userid) 
JOIN useraccounts AS us USING(userid) 
WHERE j.jobid = 123; 

我有以下問題:

  1. 的是利用語法的代名詞。語法?
  2. 這些連接從左到右進行評估嗎?換句話說,這個查詢是否說:x =公司JOIN用戶; y = x JOIN作業; z = y JOIN useraccounts;
  3. 如果問題2的答案是肯定的,那麼假設公司表中有companyid,userid和jobid列是否安全?
  4. 我不明白的WHERE子句如何被用來挑公司的錶行,當它指的是別名「J」

任何幫助,將不勝感激!

回答

23
  1. USING(fieldname)是一個簡短的說ON ON table1.fieldname = table2.fieldname的方式。

  2. SQL沒有定義JOINS完成的'順序',因爲它不是該語言的本質。很明顯,在聲明中必須指定一個命令,但是INNER JOIN可以被認爲是可交換的:您可以按任意順序列出它們,並且您將得到相同的結果。

    這就是說,當構建一個SELECT ... JOIN,尤其是包含LEFT JOIN的一個時,我發現將第三個JOIN作爲第一個JOIN的結果加入新表是有意義的,第四個加入第二個JOIN的結果,等等。

    更爲罕見的是,指定的順序可能會影響查詢優化器的行爲,因爲它影響啓發式的方式。

  3. 否。查詢的組裝方式,它要求公司和用戶都有一個companyid,jobs有一個userid和一個jobid,useraccounts有一個userid。但是,只有公司中的一個用戶需要用於JOIN工作的用戶標識。

  4. WHERE子句使用jobs表提供的列過濾整個結果 - 即所有JOINed列 - 。

0

查閱http://dev.mysql.com/doc/refman/5.0/en/join.html

並開始閱讀這裏:


加入在MySQL 5.0.12

變化處理與MySQL 5.0.12開始,自然連接,並與連接根據SQL:2003標準處理USING,包括外部連接變體。目標是根據SQL:2003根據NATURAL JOIN和JOIN ... USING調整MySQL的語法和語義。但是,連接處理中的這些更改可能會導致某些連接的不同輸出列。此外,一些似乎在舊版本中正常工作的查詢必須重寫以符合標準。

這些變化有五個主要方面:

  • ,MySQL的確定的自然科學的結果列或使用連接操作(並因此整個FROM子句的結果)的方式。

  • 將SELECT *和SELECT tbl_name。*擴展爲選定列的列表。

  • 解析NATURAL或USING連接中的列名稱。

  • 將NATURAL或USING轉換爲JOIN ... ON。

  • 在JOIN ... ON的ON條件下解析列名稱。

9

我無法回答關於USING語法的問題。這很奇怪。我從來沒有見過它,而是始終使用ON子句。

但我可以告訴你的是,JOIN操作的順序是由查詢優化器動態確定時,它構造查詢計劃,以優化啓發式,其中有些是在系統上:

  1. JOIN是否在主鍵字段上執行?如果是這樣,這在查詢計劃中獲得高優先級。

  2. JOIN是否在外鍵字段上執行?這也是重中之重。

  3. 連接字段上是否存在索引?如果是這樣,請優先處理。

  4. 對WHERE子句中的字段執行JOIN操作嗎?可以通過檢查索引來評估WHERE子句表達式(而不是執行表掃描)嗎?這是一個主要優化機會,所以它獲得了一個主要優先級凹凸。

  5. 加入列的基數是多少?具有高基數的列使優化程序有更多機會區分錯誤匹配(不符合WHERE子句或ON子句的匹配項),因此通常會在低基數連接之前處理高基數連接。

  6. 連接表中有多少實際行?加入一個只有100個值的表格將會比創建一千萬行的表格的聯合創建更少的數據爆炸。

無論如何...重點是......有很多變量進入查詢執行計劃。如果您想了解MySQL如何優化其查詢,請使用EXPLAIN語法。

這裏是一個很好的文章閱讀:

http://www.informit.com/articles/article.aspx?p=377652


上編輯:

要回答你的第4的問題:你是不是查詢 「公司」 表。您正在查詢您的FROM和USING子句中的ALL四個表的聯合交叉產品。

「j.jobid」別名只是該連接的表集合中某列的完全限定名。

+0

,這真是個跨產品?我認爲 SELECT * FROM table_a JOIN table_b USING(common_column) 會產生table_a中所有行,它們在table_b的common_column字段的任何行上都有匹配嗎?這可能少於n行。 交叉乘積不會返回多少行? – 2008-10-23 18:05:41

+0

對不起。我對USING語法一無所知,所以我不能評論它是如何工作的。 「交叉產品」的評論只是參考一般的聯接,它們有能力創建元組的組合爆炸,這就是優化器考慮基數的原因。 – benjismith 2008-10-24 17:13:40

0

林不知道有關ON VS使用部分(儘管這website說,他們是相同的)

至於排序問題,它完全實現(可能查詢)具體。在編譯請求時,MYSQL很可能會選擇一個訂單。如果你想執行一個特定的順序,你將不得不「鳥巢」您的疑問:

SELECT c.* 
FROM companies AS c 
    JOIN (SELECT * FROM users AS u 
     JOIN (SELECT * FROM jobs AS j USING(userid) 
       JOIN useraccounts AS us USING(userid) 
       WHERE j.jobid = 123) 
    ) 

爲第4部分:在where子句限制什麼從工作表中的行有資格被加入了。所以如果由於匹配用戶標識符而有些行會加入,但沒有正確的jobid,那麼它們將被省略。

0

1)使用不完全一樣,但它是手短兩個表與您要加入的相同名稱的列...看到:http://www.java2s.com/Tutorial/MySQL/0100__Table-Join/ThekeywordUSINGcanbeusedasareplacementfortheONkeywordduringthetableJoins.htm

更難以閱讀在我看來,所以我會拼出聯接。

3)從這個查詢中不清楚,但我猜測它沒有。

2)假設你是通過其他表上companyies連接(不是全部直接)在此查詢的順序並不重要...見下面的比較:

原始:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u USING(companyid) 
    JOIN jobs AS j USING(userid) 
    JOIN useraccounts AS us USING(userid) 
WHERE j.jobid = 123 

我認爲這很可能暗示:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u on u.companyid = c.companyid 
    JOIN jobs AS j on j.userid = u.userid 
    JOIN useraccounts AS us on us.userid = u.userid 
WHERE j.jobid = 123 

您合作您可以切換您的線路加入作業& usersaccounts here。

它會是什麼樣子,如果一切都加入了對公司:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u on u.companyid = c.companyid 
    JOIN jobs AS j on j.userid = c.userid 
    JOIN useraccounts AS us on us.userid = c.userid 
WHERE j.jobid = 123 

這並沒有真正的邏輯意義......除非每個用戶都擁有自己的公司。

4)SQL的神奇的是,你可以只顯示某些列,但所有的人都對自己的排序和過濾...

如果返回

SELECT c.*, j.jobid.... 

,你可以清楚地看到它過濾的是什麼,但數據庫服務器不關心你是否輸出一行或不輸入。

0

這裏是JOIN優先級更詳細的解答。在你的情況下,JOIN都是可交換的。讓我們嘗試一個他們沒有的地方。

構建模式:

CREATE TABLE users (
    name text 
); 

CREATE TABLE orders (
    order_id text, 
    user_name text 
); 

CREATE TABLE shipments (
    order_id text, 
    fulfiller text 
); 

添加數據:

INSERT INTO users VALUES ('Bob'), ('Mary'); 

INSERT INTO orders VALUES ('order1', 'Bob'); 

INSERT INTO shipments VALUES ('order1', 'Fulfilling Mary'); 

運行查詢:

SELECT * 
    FROM users 
     LEFT OUTER JOIN orders 
     ON orders.user_name = users.name 
     JOIN shipments 
     ON shipments.order_id = orders.order_id 

結果:

只有鮑勃行返回

分析:

在這個查詢中LEFT OUTER JOIN評價第一和JOIN對所LEFT OUTER JOIN的合成結果進行評價。

第二個查詢:

SELECT * 
    FROM users 
     LEFT OUTER JOIN (
     orders 
     JOIN shipments 
     ON shipments.order_id = orders.order_id) 
     ON orders.user_name = users.name 

結果:

鮑勃

一行(與履行數據)和一行瑪利亞與空值的履行數據。

分析:

括號改變了評估順序。


而且MySQL文檔是https://dev.mysql.com/doc/refman/5.5/en/nested-join-optimization.html