2010-09-18 108 views
1

我有2個表的數據庫:如何優化此查詢以擺脫子查詢?

表1:

CREATE TABLE IF NOT EXISTS `sales` (
    `sale_id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) NOT NULL, 
    `sale_total` int(11) NOT NULL, 
    `sale_date` date NOT NULL, 
    `sale_status` int(11) NOT NULL, 
    PRIMARY KEY (`sale_id`) 
) ; 

表2:

CREATE TABLE IF NOT EXISTS `users` (
    `user_id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(200) NOT NULL, 
    `lastname` varchar(200) NOT NULL, 
    `mail` varchar(200) NOT NULL, 
    PRIMARY KEY (`user_id`) 
); 

我需要下面的查詢優化,因此它不使用子查詢。我這可以使用連接來完成,但我不知道如何。

SELECT name, lastname, mail 
FROM users 
WHERE user_id IN (
    SELECT user_id 
    FROM sales 
    WHERE sale_date < '2009-01-01' 
    AND sale_total >100 
    AND sale_status =4 
) 
AND user_id NOT IN (
    SELECT user_id 
    FROM sales 
    WHERE sale_date >= '2009-01-01' 
) 

回答

3

使用連接來取代子查詢,空左聯接至IN代替NOT和GROUP BY返回只有一排爲每個用戶:

SELECT users.name, users.lastname, users.mail 
FROM users 
JOIN sales AS s0 ON s0.user_id=users.user_id 
LEFT JOIN sales AS s1 ON s1.user_id=users.user_id AND sale_date>='2009-01-01' 
WHERE s1.sale_id IS NULL 
AND s0.sale_date < '2009-01-01' AND s0.sale_total>100 AND s0.sale_status=4 
GROUP BY users.user_id 
2

怎麼樣加入:

SELECT u.name, u.lastname, u.mail FROM users u 
INNER JOIN sales s ON s.user_id = u.user_id 
WHERE s.sale_date < '2009-01-01' 
AND s.sale_total >100 
AND s.sale_status =4 

第二個查詢是unncessary - 它s.sale_date < '2009-01-01'(也許你應該解釋你詳細查詢已經beend地址):

AND user_id NOT IN (
    SELECT user_id 
    FROM sales 
    WHERE sale_date >= '2009-01-01' 
) 

如果有必要 - 它會是這樣的(未經測試):

SELECT u.name, u.lastname, u.mail FROM users u 
INNER JOIN sales s ON s.user_id = u.user_id 
RIGHT OUTER JOIN sales e ON e.user_id = u.user_id 
WHERE s.sale_date < '2009-01-01' 
AND s.sale_total >100 
AND s.sale_status =4 
AND e.sale_date >= '2009-01-01' 
AND e.user_id is null 
+0

如果第二個查詢需要的用戶沒有銷售記錄,那麼sale_date> ='2009-01-01'怎麼辦? – MStodd 2010-09-18 16:29:22

+0

這就是我現在正在考慮的問題......這部分可能無法通過連接實現。 – 2010-09-18 16:36:29

+0

感謝您的回覆!是的,我確實希望在2009-01-01之後沒有任何銷售記錄的用戶。任何想法如何避免這兩個子查詢仍然是受歡迎的。 – infrared 2010-09-18 16:48:15

0

我認爲這是解決它的表達方式。您可以使用一個連接來包含sales_status = 4等所有適當的銷售,然後使用具有having子句的第二個連接來排除在'2009-01-01'之後有銷售額的任何用戶。

性能將取決於數據庫引擎爲您的數據提出的查詢計劃,因此您應該測試它以確保在替換原始查詢之前性能更好。

select name, lastname, mail 
from users 
inner join sales s1 
    on users.user_id = s1.user_id 
     and s1.sale_date < '2009-01-01' 
     and s1.sale_total >100 
     and s1.sale_status = 4 
left join sales s2 
    on s2.user_id = users.user_id 
group by name, lastname, mail 
having max(s2.sale_date) < '2009-01-01'