2012-11-04 50 views
0

我有以下語句:使用子查詢和聯接

從誰在1997年

作出的購買所以我想通過以下兩種方式是客戶的姓名和地址的列表正確但他們不是;爲什麼?

select contactname, address from customers 
     inner join orders 
     on customers.customerid = orders.customerid 
     where date_part('year', orderdate) = 1997 
     order by contactname 

select contactname, address from customers 
     where customerid in 
      (select customerid from orders 
       where date_part('year', orderdate) = 1997) 
     order by contactname 
+0

你回來的結果有什麼問題? (我會找到另一種方法來選擇日期,但是在這樣的where子句中的列上執行函數通常是獲得表掃描的一種好方法,也許where orderdate> = 1/1/1997和orderdate <= 12/31/1997或類似的東西)。 – Marvo

+0

因爲在第一種情況下我得到了重複的結果,假設亞歷山大 - 亞歷山大 - 亞歷山大 - 阿納貝拉 - 阿納貝拉檢驗了第二個給我亞歷山大 - 阿納貝拉 - ... – FacundoGFlores

+0

我應該有在選擇和聯繫人姓名之間添加區分?我正在閱讀有關它,子查詢和聯接應該是相同的,但他們是不同的演講正確的? – FacundoGFlores

回答

4

有很多正確的方式來做到這一點。一個EXISTS半連接可能是PostgreSQL的最快的,如果你只是想從不同的訂單和客戶沒有進一步的細節清單以外,至少存在1:

SELECT c.contactname, c.address 
FROM customers c 
WHERE EXISTS (
    SELECT 1 
    FROM orders o 
    WHERE o.customerid = c.customerid 
    AND o.orderdate >= '1997-1-1'::date 
    AND o.orderdate < '1998-1-1'::date 
    ) 
ORDER BY contactname; 

爲什麼要用?

WHERE o.orderdate >= '1997-1-1'::date 
    AND o.orderdate < '1998-1-1'::date 

相反的:

WHERE date_part('year', orderdate) = 1997 

隨着你的表情的PostgreSQL將不得不計算值的每一行的條件,可以檢查之前。在替代形式中,列(按原樣)與兩個常數項匹配。這也可以更容易地使用索引。應該更快。

另請注意我如何使用table aliases使查詢更易於閱讀。

您的第一個查詢由於JOIN而遭受了行倍增。如果表customer中的某一行在表orders中有多個匹配行,則每個訂單都會有一行。你可以修復一個GROUP BY

SELECT c.contactname, c.address 
FROM customers c 
JOIN orders o USING (customerid) 
WHERE o.orderdate >= '1997-1-1'::date 
AND o.orderdate < '1998-1-1'::date 
GROUP BY c.customer_id --- or whatever is the primary key of c 
ORDER BY c.contactname 

..這是另一種方式來做到這一點。但最可能慢。你會使用這種形式,如果你也想從表orders檢索其他(彙總)數據..

DISTINCT將是一個替代GROUP BY,這樣做主要是同在這個簡單的例子。刪除GROUP BY條款並在SELECT後添加DISTINCT

您也可以使用DISTINCT修復您的第二個查詢,而是使用我的第一個示例。

+2

EXISTS不僅是最快的(因爲它是最古老的,並且已經被髮電機認可了20多年),它也是最清楚的(以犧牲相關的子查詢爲代價)。 IN變體通常是不合標準的(除去重複的原因)和危險的(由3val邏輯引起的),並且LEFT JOIN ... WHERE NULL是一個反直覺的醜陋野獸。 IMNSVHO – wildplasser

+0

@wildplasser。真實而有見地,除了你的最後一個例子。 'LEFT JOIN ... WHERE NULL'將用於* * *查詢:「查找客戶*無訂單」。這裏不適用。我最近發佈了[**比較dba.SE上的三種方法**](http://dba.stackexchange.com/a/16651/3684)(還有'LEFT JOIN .. col IS NULL')。 –

+0

糟糕,你當然是對的。重點是:我非常討厭這個構造,所以我甚至都不記得它。這是一個不存在的構造;-) *尋求藥物治療* – wildplasser