2010-09-18 110 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'