我有一个需要执行多个内部联接的SQL查询,如下所示:
SELECT DISTINCT adv.Email, adv.Credit, c.credit_id AS creditId, c.creditName AS creditName, a.Ad_id AS adId, a.adName
FROM placementlist pl
INNER JOIN
(SELECT Ad_id, List_id FROM placements) AS p
ON pl.List_id = p.List_id
INNER JOIN
(SELECT Ad_id, Name AS adName, credit_id FROM ad) AS a
ON ...
(few more inner joins)
我的问题如下:如何优化此查询?我的印象是,即使我当前查询数据库的方式创建了小型临时表(内部SELECT语句),在未更改的表上执行内部联接仍然是有利的,因为它们可能有大约10,000 - 100,000个条目(不是数百万)。但是,我被告知这不是最好的方法,但没有机会询问推荐的方法是什么。
这里最好的方法是什么?
答案 0 :(得分:2)
使用派生表,例如
INNER JOIN (SELECT Ad_id, List_id FROM placements) AS p
不推荐。让dbms自己找出它需要的值
INNER JOIN placements AS p
而不是告诉它(再次)通过强迫它仅使用两个值在表上创建视图。 (使用FROM tablename甚至更具可读性。)
使用SQL,您主要说您希望看到的,而不是 将如何实现。 (当然,这只是一个经验法则。)因此,如果在表放置中没有使用除Ad_id和List_id之外的其他列,则dbms将找到处理此问题的最佳方法。不要试图使用你的方式。
顺便说一句,IN子句的情况也是如此,您经常会看到WHERE col IN (SELECT DISTINCT colx FROM ...)
而不是WHERE col IN (SELECT colx FROM ...)
。这完全相同,但是使用DISTINCT,您可以告诉dbms“在查找col之前使子查询的行不同”。但是你为什么要强迫它这样做呢?为什么不让它只使用 dbms 最适合的方法?
返回派生表:在他们真正做某事时使用它们,尤其是聚合,或者当它们使您的查询更具可读性时。
此外,
SELECT DISTINCT adv.Email, adv.Credit, ...
也不好看。是的,有时你需要SELECT DISTINCT,但通常你不会。大多数情况下,这只是您没有考虑过查询的迹象。
一个例子:您想要选择购买产品X的客户。在SQL中您会说:为客户购买X EXISTS。或者:客户在X购买者的集合中。
select * from clients c where exists
(select * from purchases p where p.clientid = c.clientid and product = 'X');
或
select * from clients where clientid in
(select clientid from purchases where product = 'X');
你没有说:给我所有客户和X购买的组合,然后把它煮沸,这样我就可以让每个客户一次。
select distinct c.*
from clients c
join purchases p on p.clientid = c.clientid and product = 'X';
是的,只需加入所需的所有表格,然后列出要选择的列,然后将DISTINCT放在前面即可。但它使查询变得模糊,因为您不会像查询任务那样编写查询。在聚合方面,它可能会使事情变得困难。以下查询是错误的,因为您将赚取的钱数与花钱记录的数量相乘,反之亦然。
select
sum(money_spent.value),
sum(money_earned.value)
from user
join money_spent on money_spent.userid = user.userid
join money_earned on money_earned.userid = user.userid;
以下看起来可能正确,但仍然不正确(只有在值恰好是唯一的时候才有效):
select
sum(distinct money_spent.value),
sum(distinct money_earned.value)
from user
join money_spent on money_spent.userid = user.userid
join money_earned on money_earned.userid = user.userid;
再说一遍:你不会说:“我想把每次购买与每次收入结合起来然后......”。你会说:“我想要花费的金额和每位用户赚取的金额”。因此,您不是处理单笔购买或收入,而是处理其总和。如在
select
sum(select value from money_spent where money_spent.userid = user.userid),
sum(select value from money_earned where money_earned.userid = user.userid)
from user;
或者:
select
spent.total,
earned.total
from user
join (select userid, sum(value) as total from money_spent group by userid) spent
on spent.userid = user.userid
join (select userid, sum(value) as total from money_earned group by userid) earned
on earned.userid = user.userid;
所以你看,这就是派生表发挥作用的地方。