加入与子查询

You*_*nse 780 mysql sql join subquery

我是一个老派的MySQL用户,并且总是优先JOIN于子查询.但是现在每个人都使用子查询,我讨厌它; 我不知道为什么.

如果存在任何差异,我缺乏理论知识来判断自己.子查询是否与a一样好JOIN,因此没有什么可担心的?

Mar*_*tos 796

子查询是解决形式问题的逻辑上正确的方法,"从A获取事实,以B中的事实为条件".在这种情况下,在子查询中粘贴B比进行连接更合乎逻辑.从实际意义上讲,它也更安全,因为你不必因为多次匹配B而从A中获取重复的事实时要谨慎.

然而,实际上,答案通常归结为性能.一些优化器在给出连接和子查询时会吮吸柠檬,而另一些优化者则以另一种方式吮吸柠檬,这是特定于优化器,特定于DBMS的版本和查询特定的.

从历史上看,显式连接通常会获胜,因此连接的既定智慧更好,但优化器一直在变得越来越好,所以我更喜欢先以逻辑连贯的方式编写查询,然后在性能限制要求时进行重组.

  • 很好的答案.我还要补充一点,开发人员(尤其是业余开发人员)并不总是精通SQL. (98认同)
  • +1,以便在快速之前将其正确设置。 (69认同)
  • @JinghuiNiu购买贵物品的顾客:`选择custid从cust join使用(custid)购买,价格> 500`.如果顾客购买了多件昂贵的物品,你会得到双倍的打击.为了解决这个问题,`从存在的select select custid(select*from buy where custid = cust.custid and price> 500)`.您可以使用`select distinct ...'来代替,但对于优化程序或评估程序来说,它通常更有用. (6认同)
  • +1很长一段时间寻找这个问题的一些合理解释,这只是对我而言合乎逻辑的答案 (4认同)
  • @Marcelo Cantos,您能否举一个您的陈述的例子“从实际意义上讲,它也更安全,因为您不必因为与 B 的多次比赛而从 A 获得重复的事实”?我发现这非常有见地,但有点太抽象了。谢谢。 (4认同)

Kro*_*ass 354

在大多数情况下,JOINs比子查询更快,并且子查询的速度非常快.

JOINRDBMS中可以创建一个更适合您的查询的执行计划,并且可以预测应该加载哪些数据以进行处理并节省时间,这与子查询不同,子查询将运行所有查询并加载所有数据以进行处理.

子查询的好处是它们比JOINs 更具可读性:这就是大多数新SQL用户更喜欢它们的原因; 这是简单的方法; 但是在性能方面,JOINS在大多数情况下都更好,即使它们也不难读.

  • 我对包含对上层查询的反向引用的子查询有很好的体验,特别是当涉及到超过100,000的行数时.事情似乎是内存使用和交换文件的分页.连接会产生大量数据,这些数据可能不适合内存,必须分页到交换文件中.只要是这种情况,小的子选择的查询时间,例如`select*from a where ax =(select bx form b where b.id = a.id)`与连接相比非常小.这是一个非常具体的问题,但在某些情况下,它会带您从几小时到几分钟. (61认同)
  • 你能用文件参考或测试结果证明你的任何要点吗? (21认同)
  • 对于提出的问题,这个答案有点过于简单.正如您所述:某些子查询是正常的,而某些子查询则没有.答案并没有真正帮助区分这两者.(也是'非常罕见'真的取决于你的数据/应用程序). (16认同)
  • 是的,因此大多数数据库都将其作为优化步骤,在分析查询时将子查询转换为连接. (14认同)
  • 我对Oracle很有经验,我可以说,如果您没有对它们进行任何过滤或排序,则子查询在大型表上要好得多. (12认同)

sim*_*eco 162

摘自MySQL手册(13.2.10.11重写子查询作为连接):

LEFT [OUTER] JOIN可以比等效的子查询更快,因为服务器可能能够更好地优化它 - 这一事实并非仅针对MySQL Server.

因此子查询可能比LEFT [OUTER] JOINS慢,但在我看来,它们的强度可读性稍高.

  • 性能比可读性更重要. (69认同)
  • @ user1735921它取决于IMO ......一般来说,代码的可读性非常重要,因为它对以后的管理非常重要......让我们记住Donald Knuth的着名陈述:*"过早优化是其根源编程中的所有邪恶(或至少大部分)"*.然而,自然有一些编程领域,性能是至关重要的...理想情况下,当一个成功地协调一个与另一个:) (42认同)
  • 在更复杂的查询中,我发现连接比子查询更容易阅读.子查询变成了我脑袋里的一碗面条. (27认同)
  • @ user1735921当然,特别是当查询变得如此复杂以至于它做错了而你花了一天时间来修复它...它们之间有平衡,像往常一样. (6认同)
  • @ user1735921只有当性能提升值得将来所需的维护时间增加时 (6认同)
  • 联接具有更高的可读性和性能,但如果您在查询中使用太多联接(如 5-10),最好考虑进行一些优化并分解为两个查询或子查询,这将提高性能和可读性,这就是我从我的经验中学到的。 (2认同)
  • 我的观点`Join`和`sub query`有不同的语法,所以我们无法比较的可读性,只要你的SQL语法很好,它们都具有更高的可读性.表现更重要. (2认同)
  • 真实故事,join 比子查询快 (2认同)

Fra*_*ens 126

使用EXPLAIN查看数据库如何对数据执行查询.在这个答案中有一个巨大的"取决于"......

当PostgreSQL认为一个子查询比另一个更快时,它可以将子查询重写为连接或子查询的连接.这一切都取决于数据,索引,相关性,数据量,查询等.

  • 这正是postgresql如此优秀和有用的原因,它了解目标是什么,并将基于它认为更好的方式修复查询,postgresql非常善于知道如何查看其数据 (6认同)

Tre*_*her 51

在2010年,我本来会加入这个问题的作者,并会有强烈的投票支持JOIN.但是有了更多的经验(特别是在MySQL中)我可以说:是的子查询可以更好.我在这里读过多个答案.有人说,子查询更快,但缺乏一个很好的解释.我希望我能提供这个(非常)迟到的答案:

首先,让我说最重要的是:有不同形式的子查询

第二个重要声明:规模问题

如果您使用子查询,您应该知道,DB-Server如何执行子查询.特别是如果子查询被评估一次或每行!另一方面,现代DB-Server能够进行大量优化.在某些情况下,子查询有助于优化查询,但较新版本的DB-Server可能会使优化过时.

选择字段中的子查询

SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo
Run Code Online (Sandbox Code Playgroud)

请注意,对每个结果行执行子查询foo.尽可能避免这种情况,它可能会大大减慢对大型数据集的查询速度.但是如果子查询没有引用foo,它可以由DB服务器优化为静态内容,并且只能被评估一次.

Where语句中的子查询

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)
Run Code Online (Sandbox Code Playgroud)

如果幸运的话,DB会在内部对此进行优化JOIN.如果没有,您的查询将在大型数据集上变得非常非常慢,因为它将为每一行执行子查询foo,而不仅仅是select-type中的结果.

Join语句中的子查询

SELECT moo, bar 
  FROM foo 
    LEFT JOIN (
      SELECT MIN(bar), me FROM wilco GROUP BY me
    ) ON moo = me
Run Code Online (Sandbox Code Playgroud)

这是有趣的.我们结合JOIN了一个子查询.在这里,我们得到了子查询的真正优势.想象一下,数据集中包含数百万行,wilco但只有少数几行me.我们现在有一个较小的临时表来加入,而不是加入一个巨大的表.这可以导致更快的查询,具体取决于数据库大小.您可以使用CREATE TEMPORARY TABLE ...和使用相同的效果INSERT INTO ... SELECT ...,这可以在非常复杂的查询上提供更好的可读性(但可以将数据集锁定在可重复的读隔离级别).

嵌套的子查询

SELECT moo, bar
  FROM (
    SELECT moo, CONCAT(roger, wilco) AS bar
      FROM foo
      GROUP BY moo
      HAVING bar LIKE 'SpaceQ%'
  ) AS temp_foo
  ORDER BY bar
Run Code Online (Sandbox Code Playgroud)

您可以在多个级别中嵌套子查询.如果您必须对结果进行分组或排序,这可以对大型数据集有所帮助.通常,DB-Server为此创建一个临时表,但有时您不需要对整个表进行排序,只需对结果集进行排序.这可能会提供更好的性能,具体取决于表的大小.

结论

子查询不能代替a JOIN,你不应该像这样使用它们(尽管可能).在我看来,正确使用子查询是用作快速替代CREATE TEMPORARY TABLE ....一个好的子查询以某种方式减少数据集,你无法在一个ON语句中完成JOIN.如果子查询具有其中一个关键字,GROUP BY或者DISTINCT最好不在select字段或where语句中,那么它可能会大大提高性能.

  • 对于`Join-statement 中的子查询`: (1) 从子查询本身生成派生表可能需要很长时间。(2) 生成的派生表没有索引。仅这两个就可以显着减慢 SQL。 (3认同)
  • 我真的很喜欢你的解释。谢谢。 (2认同)

Unr*_*son 41

首先,要比较两者,首先应将查询与子查询区分开来:

  1. 一类子查询,始终具有使用连接编写的相应等效查询
  2. 一类无法使用连接重写的子查询

对于第一类查询,良好的RDBMS将联接和子查询视为等效,并将生成相同的查询计划.

这些天甚至mysql都这样做.

尽管如此,有时却没有,但这并不意味着连接总是会赢 - 我在mysql中使用子查询提高了性能.(例如,如果存在阻止mysql规划器正确估计成本的事情,并且规划器没有看到连接变量和子查询变量相同,那么子查询可以通过强制某个路径来胜过连接).

结论是,如果要确定哪个更好,那么您应该测试连接和子查询变体的查询.

对于第二个类,比较没有意义,因为这些查询不能使用连接重写,在这些情况下,子查询是执行所需任务的自然方式,您不应该区别它们.

  • 您能否提供一个使用无法转换为联接的子查询编写的查询的示例(第二类,如您所说)? (3认同)

Uğu*_*han 22

SQL Server的MSDN文档说

包含子查询的许多Transact-SQL语句也可以表示为连接.其他问题只能通过子查询提出.在Transact-SQL中,包含子查询的语句与不包含子查询的语义等效版本之间通常没有性能差异.但是,在某些必须检查存在的情况下,连接会产生更好的性能.否则,必须为外部查询的每个结果处理嵌套查询,以确保消除重复项.在这种情况下,联接方法会产生更好的结果.

所以,如果你需要类似的东西

select * from t1 where exists select * from t2 where t2.parent=t1.id
Run Code Online (Sandbox Code Playgroud)

尝试使用连接.在其他情况下,它没有任何区别.

我说:为子查询创建函数消除了cluttter的问题,并允许您为子查询实现额外的逻辑.所以我建议尽可能为子查询创建函数.

代码杂乱是一个大问题,业界几十年来一直在努力避免它.

  • 在某些RDBMS(例如Oracle)中,用函数替换子查询是一个非常糟糕的想法,所以我建议恰恰相反 - 尽可能使用子查询/连接而不是函数. (9认同)
  • @FrankSchmitt请用引用支持你的论点. (3认同)
  • 即使您检查是否存在,也有一些情况下您应该使用子查询而不是连接:如果您检查"NOT EXISTS".由于各种原因,"NOT EXISTS"赢得了"LEFT OUTER JOIN":性能,故障安全(如果是nulable列)和可读性.http://www.sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join (2认同)

pka*_*mol 21

我认为引用答案中未充分强调的是特定(使用)案例可能产生的重复和问题结果的问题.

(虽然Marcelo Cantos确实提到过)

我将引用斯坦福大学关于SQL的Lagunita课程的例子.

学生表

+------+--------+------+--------+
| sID  | sName  | GPA  | sizeHS |
+------+--------+------+--------+
|  123 | Amy    |  3.9 |   1000 |
|  234 | Bob    |  3.6 |   1500 |
|  345 | Craig  |  3.5 |    500 |
|  456 | Doris  |  3.9 |   1000 |
|  567 | Edward |  2.9 |   2000 |
|  678 | Fay    |  3.8 |    200 |
|  789 | Gary   |  3.4 |    800 |
|  987 | Helen  |  3.7 |    800 |
|  876 | Irene  |  3.9 |    400 |
|  765 | Jay    |  2.9 |   1500 |
|  654 | Amy    |  3.9 |   1000 |
|  543 | Craig  |  3.4 |   2000 |
+------+--------+------+--------+
Run Code Online (Sandbox Code Playgroud)

申请表

(向特定大学和专业提出的申请)

+------+----------+----------------+----------+
| sID  | cName    | major          | decision |
+------+----------+----------------+----------+
|  123 | Stanford | CS             | Y        |
|  123 | Stanford | EE             | N        |
|  123 | Berkeley | CS             | Y        |
|  123 | Cornell  | EE             | Y        |
|  234 | Berkeley | biology        | N        |
|  345 | MIT      | bioengineering | Y        |
|  345 | Cornell  | bioengineering | N        |
|  345 | Cornell  | CS             | Y        |
|  345 | Cornell  | EE             | N        |
|  678 | Stanford | history        | Y        |
|  987 | Stanford | CS             | Y        |
|  987 | Berkeley | CS             | Y        |
|  876 | Stanford | CS             | N        |
|  876 | MIT      | biology        | Y        |
|  876 | MIT      | marine biology | N        |
|  765 | Stanford | history        | Y        |
|  765 | Cornell  | history        | N        |
|  765 | Cornell  | psychology     | Y        |
|  543 | MIT      | CS             | N        |
+------+----------+----------------+----------+
Run Code Online (Sandbox Code Playgroud)

让我们试着找一些申请CS专业(不论大学)的学生的GPA分数

使用子查询:

select GPA from Student where sID in (select sID from Apply where major = 'CS');

+------+
| GPA  |
+------+
|  3.9 |
|  3.5 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+
Run Code Online (Sandbox Code Playgroud)

此结果集的平均值为:

select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');

+--------------------+
| avg(GPA)           |
+--------------------+
| 3.6800000000000006 |
+--------------------+
Run Code Online (Sandbox Code Playgroud)

使用连接:

select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+------+
| GPA  |
+------+
|  3.9 |
|  3.9 |
|  3.5 |
|  3.7 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+
Run Code Online (Sandbox Code Playgroud)

此结果集的平均值:

select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+-------------------+
| avg(GPA)          |
+-------------------+
| 3.714285714285714 |
+-------------------+
Run Code Online (Sandbox Code Playgroud)

很明显,第二次尝试在我们的用例中产生了误导性结果,因为它计算重复计算平均值.同样显而易见的是,使用distinct基于连接的语句不会消除问题,因为它会错误地保留三分之一的3.9分数.正确的情况是考虑到我们实际上有两(2)名学生的分数符合我们的查询标准,因此考虑了分数的两(2)次出现.3.9

在某些情况下,除了任何性能问题之外,在某些情况下,子查询是最安全的方式.

  • 我认为你不能在这里使用子查询。从逻辑上讲,这不是一种可以使用任何一种的情况,但由于其技术实现,人们会给出错误的答案。在这种情况下,您不能使用子查询,因为不属于 CS 的学生可以获得 3.9 分,该分数位于 IN 分数列表中。一旦执行子查询,CS的上下文就会丢失,这在逻辑上不是我们想要的。所以这不是一个可以使用两者的好例子。对于此用例,子查询的使用在概念/逻辑上是错误的,即使幸运的是它为不同的数据集提供了正确的结果。 (3认同)

小智 16

在旧的Mambo CMS上运行一个非常大的数据库:

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );
Run Code Online (Sandbox Code Playgroud)

0秒

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;
Run Code Online (Sandbox Code Playgroud)

~3秒

EXPLAIN显示它们检查完全相同的行数,但一个需要3秒,一个接近瞬间.故事的道德启示?如果性能很重要(何时不是?),请尝试多种方式,看看哪一个最快.

和...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;
Run Code Online (Sandbox Code Playgroud)

0秒

同样,相同的结果,检查的行数相同.我的猜测是,DISTINCT mos_content.catid需要比DISTINCT mos_categories.id更长的时间来计算.

  • 在这种情况下使用 SQL IN 是一种不好的做法,它不能证明任何事情。 (2认同)

rku*_*lla 12

子查询通常用于将单个行作为原子值返回,但它们可用于将值与多个行与IN关键字进行比较.它们几乎可以在SQL语句中的任何有意义的点上使用,包括目标列表,WHERE子句等.可以使用简单的子查询作为搜索条件.例如,在一对表之间:

   SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');
Run Code Online (Sandbox Code Playgroud)

请注意,对子查询的结果使用常规值运算符要求只返回一个字段.如果您对检查一组其他值中是否存在单个值感兴趣,请使用IN:

   SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');
Run Code Online (Sandbox Code Playgroud)

这显然不同于LEFT-JOIN,你只想加入表A和B中的东西,即使连接条件没有在表B中找到任何匹配的记录,等等.

如果您只是担心速度,则必须检查数据库并编写一个好的查询,看看性能是否有任何显着差异.


小智 12

根据我的观察,如两个案例,如果一个表有少于100,000个记录,那么连接将快速工作.

但是如果一个表有超过100,000个表,那么子查询是最好的结果.

我有一个表,我在下面的查询中创建了500,000条记录,其结果时间就像

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;
Run Code Online (Sandbox Code Playgroud)

结果:13.3秒

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)
Run Code Online (Sandbox Code Playgroud)

结果:1.65秒

  • 如何比较返回不同结果的两个查询的性能有什么意义? (14认同)
  • @anikislamshojib 仔细看看:我们在第一个语句中有“*”和两个表,但在第二个语句中只有一个表,因此 *I* 预计列数会有所不同。 (2认同)

aru*_*run 11

MySQL版本:5.5.28-0ubuntu0.12.04.2-log

我还认为JOIN总是比MySQL中的子查询更好,但EXPLAIN是一种更好的判断方式.这是一个子查询比JOIN更好的例子.

这是我的3个子查询的查询:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;
Run Code Online (Sandbox Code Playgroud)

EXPLAIN显示:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
Run Code Online (Sandbox Code Playgroud)

与JOIN相同的查询是:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;
Run Code Online (Sandbox Code Playgroud)

输出是:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

rows列的比较告诉了差异,并且JOIN的查询正在使用Using temporary; Using filesort.

当然,当我运行两个查询时,第一个查询在0.02秒内完成,第二个查询在1分钟后仍未完成,因此EXPLAIN正确解释了这些查询.

如果我没有list_tag桌面上的INNER JOIN,即如果我删除

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  
Run Code Online (Sandbox Code Playgroud)

从第一个查询和相应的:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403
Run Code Online (Sandbox Code Playgroud)

从第二个查询开始,EXPLAIN为两个查询返回相同的行数,这两个查询的运行速度相同.


Vla*_*lad 11

子查询具有即时计算聚合函数的能力.例如,查找该书的最低价格,并获得以此价格出售的所有书籍.1)使用子查询:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);
Run Code Online (Sandbox Code Playgroud)

2)使用JOIN

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;
Run Code Online (Sandbox Code Playgroud)

  • -1这是误导性的,因为您正在使用子查询并加入这两个示例.那你拉子查询出到第二查询,以确定的最低订单价格有因为数据库会做同样的事情没有影响.另外,你不重写加入使用子查询; 这两个查询使用连接.*您是*正确的,子查询支持聚合功能,但这个例子不能证明这一事实. (6认同)

fic*_*489 8

  • 一般规则是,在大多数情况下,连接速度更快(99%)。
  • 数据表越多,子查询就越慢。
  • 数据表的数量越少,子查询的速度与joins相等。
  • 子查询更简单,更容易理解,更容易阅读。
  • 大多数Web和应用程序框架及其“ ORM”和“活动记录”都会生成带有查询的查询,因为使用子查询更容易划分职责,维护代码等。
  • 对于较小的网站或应用程序,子查询是可以的,但是对于较大的网站和应用程序,您通常将不得不重写生成的查询以加入查询,特别是如果查询在查询中使用了许多查询。

有人说“一些RDBMS可以重写一个子查询加入加入到一个子查询时,它认为一个比其他的要快。”,但这句话适用于简单的情况下,肯定不会对于复杂查询与子查询实际上事业性能问题。