2012-12-18 87 views
0

我試圖運行查詢來選擇客戶受衆,但應該選擇之前沒有收到電子郵件的客戶。電子郵件跟蹤來自另一個表。這是原始查詢:SQL查詢在哪裏不同

SELECT 
    c.customers_firstname, 
    c.customers_lastname, 
    o.orders_id, 
    o.customers_id, 
    c.customers_email_address 
FROM 
    orders o, 
    customers c, 
    order_status s 
WHERE 
    o.customers_id = c.customers_id 
    AND o.orders_id = s.orders_id 
    AND o.orders_status = s.orders_status_id 
ORDER BY 
    o.orders_id ASC 

現在,我需要檢查另一個表稱爲跟蹤,看看如果客戶在該表中,如果是已經存在,跳過它。

這是我嘗試過,但似乎並沒有工作:

SELECT 
    c.customers_firstname, 
    c.customers_lastname, 
    o.orders_id, 
    o.customers_id, 
    c.customers_email_address 
FROM 
    orders o, 
    customers c 
INNER JOIN 
    tracking t 
ON 
    c.customers_id = t.customers_id, 
    order_status s 
WHERE 
    o.customers_id = c.customers_id 
    AND o.orders_id = s.orders_id 
    AND o.orders_status = s.orders_status_id 
    AND c.customers_id NOT LIKE t.customers_id 
ORDER BY 
    o.orders_id ASC 

我在做什麼錯?或者有什麼辦法可以做得更好?

增加:我完全忘了一個更重要的因素 - 跟蹤表有「模塊」列,我只需要從「聯繫」模塊的結果。換句話說,我需要過濾掉已經存在於追蹤表中的客戶,但只有與聯繫人模塊關聯,而不是與其他任何模塊關聯。

+1

不要混合使用隱性和顯性連接。 – zerkms

回答

1

這等同於原始查詢:

SELECT c.customers_firstname 
    , c.customers_lastname 
    , o.orders_id 
    , o.customers_id 
    , c.customers_email_address 
    FROM orders o 
    JOIN customers c 
    ON c.customers_id = o.customers_id 
    JOIN order_status s 
    ON s.orders_id = o.orders_id 
    AND s.orders_status_id = o.orders_status 
ORDER 
    BY o.orders_id ASC 

添加抗加入

要符合您的要求,您可以使用「反連接」的格局。我們可以order by子句前添加這個到查詢,:

LEFT 
    JOIN tracking t 
    ON t.customers_id = o.customers_id 
WHERE t.customers_id IS NULL 

那是什麼要做的是找到了tracking表中的所有匹配行的基礎上,customers_id。對於查詢在tracking table中找不到匹配行的任何行,它將從tracking生成一個由所有NULL值組成的虛擬行。 (這是描述OUTER JOIN做什麼的一種方式。)

現在「訣竅」是拋出所有匹配的行。我們通過從跟蹤表(在WHERE子句中)檢查customers_id的NULL值來實現這一點。對於匹配,該列不會爲NULL。 (在連接謂詞中的等價比較可以保證我們。)所以我們知道如果我們得到t.customers_id的NULL值,那就沒有匹配。

所以,這個查詢返回指定的結果集:

SELECT c.customers_firstname 
    , c.customers_lastname 
    , o.orders_id 
    , o.customers_id 
    , c.customers_email_address 
    FROM orders o 
    JOIN customers c 
    ON c.customers_id = o.customers_id 
    JOIN order_status s 
    ON s.orders_id = o.orders_id 
    AND s.orders_status_id = o.orders_status 
    LEFT 
    JOIN tracking t 
    ON t.customers_id = o.customers_id 
WHERE t.customers_id IS NULL 
ORDER 
    BY o.orders_id ASC 

其他方法

還有其它的方法,但是反連接經常表現最佳。

其他一些選項是謂詞和謂詞NOT IN謂詞。我可以添加這些內容,但我希望這些解決方案將在我接觸到它之前在其他答案中提供。


與第一查詢(相當於在你的問題中查詢)開始,我們也可以用NOT EXISTS謂語。我們會在訂單之前添加此BY子句:

WHERE NOT EXISTS 
     (SELECT 1 
      FROM tracking t 
      WHERE t.customers_id = o.customers_id 
     ) 

要使用NOT IN謂詞,再次,訂單之前添加此BY子句:

WHERE o.customers_id NOT IN 
     (SELECT t.customers_id 
      FROM tracking t 
      WHERE t.customers_id IS NOT NULL 
     ) 

(您可能有一些保證跟蹤。 customers_id不爲空,但在更一般的情況下,重要的是子查詢NOT不返回NULL值,所以我們包含WHERE子句以便保證。)

使用適當的索引,反連接模式通常表現比更好或NOT IN,但並不總是如此。

+1

好吧,你多走了一圈,所以我刪除了我的答案,因爲它覆蓋了同一個地方。你可以包括[這個鏈接來解釋擴展](http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/),它討論查詢計劃如何不同。你也可以討論爲什麼原始查詢不起作用 –

+0

@Conrad Frix:我不是故意重寫你的答案,我對此表示歉意。我只想解釋反連接模式,並提供一些替代方案。該鏈接提供了有關這些差異的相當好的寫法。 – spencer7593

+0

不用擔心:)你的回答使我的畫面變得多餘,變成了噪音。僅僅因爲我的年紀大了幾分鐘並不意味着它應該留下。 –

0

像spencer7593建議你可以做反連接但不是

LEFT JOIN tracking t ON t.customers_id = o.customers_id 
WHERE t.customers_id IS NULL 

你可以寫容易

JOIN tracking t ON t.customers_id = o.customers_id