使用许多内部联接构建SQL查询的最佳方法是什么?

时间:2014-11-03 08:19:14

标签: mysql sql query-optimization inner-join

我有一个需要执行多个内部联接的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个条目(不是数百万)。但是,我被告知这不是最好的方法,但没有机会询问推荐的方法是什么。

这里最好的方法是什么?

1 个答案:

答案 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;

所以你看,这就是派生表发挥作用的地方。