2013-03-20 68 views
3

我想知道爲什麼這個查詢仍然完美。我認爲WHERE子句總是以WHERE開頭?特有的行爲:AND沒有在哪裏仍然有效

SELECT `persons`.* 
    FROM `persons` 
    LEFT JOIN `team_memberships` 
    ON (`team_memberships`.`participant` = `persons`.`id`) 
    JOIN `teams` 
    ON (`teams`.`id` = `team_memberships`.`team`) 
    JOIN `departments` 
    ON (`departments`.`id` = `teams`.`department`) 
    JOIN `areas` 
    ON (`areas`.`id` = `departments`.`area`) 
    JOIN `companies` 
    ON (`companies`.`id` = `areas`.`company`) 

    [NO WHERE HERE] 

    AND `persons`.`id` = ? 
+0

也許連接充當隱式的WHERE?在T-SQL中,連接可以用WHERE子句編寫,所以這可能是相反的行爲。 – 2013-03-20 14:56:12

+0

我不確定,但我認爲AND屬於最後一個JOIN關鍵字: 'JOIN companies ON(companies.id = areas.company)AND persons.id =?'。 – 2013-03-20 14:58:20

+0

好吧,加入AND條件意味着什麼?我應該跳過哪裏?順便說一下,這是偶然發生的,它原本應該在那裏。 – silkfire 2013-03-20 14:59:10

回答

6

你剛剛增加了一個ANDON條款。 ON子句只是計算布爾值true或false,因此ANDOR布爾操作符是允許的,所有常用的關於括號等的規則也適用。

將特定於特定聯接表的條件從您的WHERE子句移動到合適的ON子句中 - 除非爲了提高可讀性而沒有其他原因,這通常是有利的。

它可以使邏輯*感之前,以限制參加結果回事加入其它表,而不是返回一個大的結果集,然後在用WHERE條款最終篩選這一點。我說*合乎邏輯,因爲DBMS通常會優化您的查詢,以至於無論如何這會發生......但並非總是......有時執行不佳的查詢可通過在連接處進行篩選來改進(有時情況則相反)。

例如,如果你知道你只有在人的25歲以上的有興趣的,是有意義的JOIN persons ON persons.id = team_memberships.participant AND persons.age > 25,而不是包括隨後的表只屬於年輕的小混混才所有結果在最後再用WHERE persons.age > 25子句過濾這些結果。

我的理解是,連接性能是可能降低作爲ON子句中另外的表達(S)的結果是,在該實例,其中另外的表達(或多個):

  • 導致評價(因此我上面的age > 25示例可能在此類別中)或

  • 依賴來自一個/兩個連接表中的非索引列,而連接本來只依賴於索引列。

所以,例如,它可能仍然證明是值得從結果集,如果它使特定的加入更慢,因爲較小的結果集,在這一點上可能會爲後續大的性能提升的加入甚至剝離年輕小混混連接。但是,如果它們包含在連接中只會使記錄數量稍微增加,那麼將它們包含在連接中然後在WHERE子句中將其過濾掉可能會更快。

但我歡迎澄清/糾正我的理解。


編輯:按照@ITroubs下面發表評論,我真的應該明確的是,如果連接是一個INNER JOIN那麼最終的結果集將是相同的,無論是否附加的過濾條件是ONWHERE子句中的,但是例如在OP的原始示例中,如果過濾條件從WHERE移動到LEFT JOIN ... ON ...LEFT JOIN ON team_memberships將產生完全不同的結果集。

+0

如果是這樣的話,那麼我應該在哪裏放置自己的狀況,因爲它屬於表「人」,原始表? – silkfire 2013-03-20 15:04:30

+0

非常感謝。將查詢更改爲:ON(persons.id =?AND team_memberships.participant = persons.id)'。明智的答案。 – silkfire 2013-03-20 15:06:55

+0

@silkfire如果將它放在'WHERE'子句中是有意義的,那就這樣做。但是,如果你將它移動到'LEFT JOIN team_memberships ON team_memberships.participant = persons.id AND persons.id =?',你會看到你可以(我認爲*)規範化爲'LEFT JOIN team_memberships ON team_memberships.participant = ?'。我說*「我認爲」是因爲我對MySQL不是很熟悉,所以我不能100%確定'?'是什麼意思... ;-) – Sepster 2013-03-20 15:07:33

2

如果我沒有記錯的話那麼MySQL執行查詢,如下所示:

SELECT `persons`.* 
FROM `persons` 
LEFT JOIN `team_memberships` 
ON (`team_memberships`.`participant` = `persons`.`id`) 
JOIN `teams` 
ON (`teams`.`id` = `team_memberships`.`team`) 
JOIN `departments` 
ON (`departments`.`id` = `teams`.`department`) 
JOIN `areas` 
ON (`areas`.`id` = `departments`.`area`) 
JOIN `companies` 
ON ((`companies`.`id` = `areas`.`company`) AND `persons`.`id` = ?') 
0

最後,而不是WHERE子句中的一部分,它的JOIN子句

0

它的工作原理,因爲你實際上並沒有一個WHERE條款,你的條件AND persons.id = ?是最後JOIN

0

部分的一部分,它實際上意味着

join `companies` on ON (`companies`.`id` = `areas`.`company`) AND persons.id = ? 

所以,既然要執行內部連接 ​​- 這幾乎是* * equvivalent。只要您切換到計算機連接或複雜的查詢 - 一切都會中斷。