2017-07-24 70 views
1

我查了很多類似的問題,但沒有人適用於Firebird,我想。如何選擇連接的行,即使沒有匹配?

我有兩個表;一個存儲客戶信息,第二個存儲庫存活動(其中還包括訂單)。我想提取所有客戶以及他們訂單的數量。但無論我如何加入訂單表,我最終只得到至少有一個訂單的客戶。這意味着在庫存活動表中沒有匹配的客戶將不會顯示在結果集中。

這是我運行的查詢;

SELECT 
    C.NAME, C.GROUPNAME, C.EMAIL, 
    COALESCE(COUNT(DISTINCT S.ORDERNO), '0') AS TOTALORDERS, 
    COALESCE(SUM(S.AMOUNT), '0') as TOTALREVENUE 
FROM CUSTOMERS C 
LEFT OUTER JOIN STOCK_ACTIVITY S ON C.ID = S.CUSTOMERID 
WHERE C.GROUPNAME = 'B' 
    AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE') 
GROUP BY C.NAME, C.GROUPNAME, C.EMAIL 

沒有連接,我得到570行(客戶),它是正確的結果集。當我加入訂單表以獲取這些客戶的總訂單量時;我只得到379個結果;哪些是至少有一個訂單。這意味着沒有訂單的客戶將不會退貨。你可能已經猜到了;我想讓零活動的客戶返回「0」作爲訂單金額和收入。

回答

4

問題是您的WHERE子句過濾「右手」表的值。

WHERE ... 
    AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE') 

當外部聯接生成左表中的「不匹配」行記錄,它右表中的所有列提供NULL值。所以S.TYPENULL這些記錄。

有兩種可能的解決方案:

  1. 明確允許在WHERE邏輯「NULL記錄」的情況。

按照某些標準,這可能在將連接條件從過濾器中分離出來時「更純」,但它可能會相當複雜(因此容易出錯)。需要注意的一個問題是,您可能需要將生成的NULL記錄與恰好具有一些NULL數據的右表的「真實」記錄區分開來。

測試連接鍵的右表值爲NULL應該是合理安全的。你可以測試右表的PK值爲NULL(假設你在該表上有一個真正的PK)。

  1. 將謂詞從WHERE子句移到外連接的ON子句。

這是非常簡單的,看起來像

SELECT C.NAME, C.GROUPNAME, C.EMAIL, 
     COALESCE(COUNT(DISTINCT S.ORDERNO), '0') AS TOTALORDERS, 
     COALESCE(SUM(S.AMOUNT), '0') as TOTALREVENUE 
    FROM     CUSTOMERS C 
     LEFT OUTER JOIN STOCK_ACTIVITY S 
        ON C.ID = S.CUSTOMERID 
        AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE') 
WHERE C.GROUPNAME = 'B' 
GROUP BY C.NAME, C.GROUPNAME, C.EMAIL 

試圖匹配他們對CUSTOMERS記錄(這意味着NULL記錄之前,這將有效地過濾STOCK_ACTIVITY記錄呈現給加盟仍然可以無干擾產生)。 (「有效地」,因爲如你知道DBMS將遵循什麼步驟說話是愚蠢的;我們所能說的是,這具有與通過某些步驟獲得的相同的效果...)

+0

謝謝;這已經完成了這項工作。雖然兩種不同的選擇產生了不同的結果。有趣的是,將過濾器連接到連接中產生比where子句少2行的內容。 – Turab

+0

這可能意味着'STOCK_ACTIVITY'中有2條記錄,'TYPE'真*是*'NULL'。我想我應該指出,如果使用選項1,則需要將「真實記錄與某些」NULL「值區分開」形成「生成的」NULL記錄「。在大多數敏感的數據模型中,您可以通過檢查右表PK值是否爲NULL來做到這一點。或者肯定你可以測試連接鍵的右表值是否爲NULL(因爲an = join不會匹配這樣的記錄) –

-1

保持聚合操作與JOIN分離。這是最乾淨的。首先進行分組,然後加入附加信息。

+0

沒有解決問題,並且取決於DBMS可能是性能不佳的明智建議 –

+0

您是對的,但500行對於性能的觀點來說並不重要,而間隔信息可以幫助理解邏輯 - 更好的學習曲線比寫解決方案。沒有惡意。 –

+0

您的意見被記錄下來,但這是一個問題和答案網站,而不是一個問題,並告訴你,我認爲是錯誤的,你要問它的網站。 –

0

如果沒有STOCK_ACTIVITY對於CUSTOMER,則會連接一條滿是NULL的行。這也意味着WHERE陳述AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')永遠不可能適用於這些行。

相關問題