我有一个包含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'
)
答案 0 :(得分:3)
使用连接替换IN子查询,使用null-left-joins替换NOT IN,使用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
答案 1 :(得分: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
第二个查询是不必要的 - 它已经使用s.sale_date&lt; '2009-01-01'(也许你应该详细解释你的查询):
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
答案 2 :(得分: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'