我是一个老派的MySQL用户,总是更喜欢JOIN而不是子查询。但是现在每个人都用子查询,我讨厌它;我不知道为什么。
我缺乏理论知识来判断是否有任何不同。子查询是否与JOIN一样好,因此没有什么可担心的?
我是一个老派的MySQL用户,总是更喜欢JOIN而不是子查询。但是现在每个人都用子查询,我讨厌它;我不知道为什么。
我缺乏理论知识来判断是否有任何不同。子查询是否与JOIN一样好,因此没有什么可担心的?
当前回答
根据我的观察,就像两种情况,如果一个表的记录少于10万条,那么连接将工作得很快。
但是如果一个表有超过100,000条记录,那么子查询是最好的结果。
我有一个表,其中有500,000条记录,我在查询下面创建了它,它的结果时间是
SELECT *
FROM crv.workorder_details wd
inner join crv.workorder wr on wr.workorder_id = wd.workorder_id;
结果:13.3秒
select *
from crv.workorder_details
where workorder_id in (select workorder_id from crv.workorder)
结果:1.65秒
其他回答
我只是在考虑同样的问题,但我在FROM部分使用子查询。 我需要连接和查询大表,“从”表有2800万条记录,但结果只有128个这样小的结果大数据!我在它上面使用MAX()函数。
首先,我使用LEFT JOIN,因为我认为这是正确的方式,mysql可以优化等。 第二次只是为了测试,我重写了针对JOIN的子选择。
LEFT JOIN运行时:1.12s SUB-SELECT运行时间:0.06秒
子选择比连接快18倍!只是在chokito广告。subselect看起来很糟糕,但结果…
子查询是解决“从A获取事实,以B的事实为条件”这种形式的问题的逻辑正确方法。在这种情况下,在子查询中插入B比进行连接更具逻辑意义。从实际意义上讲,它也更安全,因为您不必担心由于与B的多个匹配而从a获得重复的事实。
然而,实际上,答案通常归结于性能。当给出连接和子查询时,一些优化器会很糟糕,而另一些则相反,这是特定于优化器、特定于dbms版本和特定于查询的。
从历史上看,显式连接通常会胜出,因此已经建立的智慧是连接更好,但优化器一直在变得更好,因此我更喜欢先以逻辑一致的方式编写查询,然后在性能限制的情况下重新构造查询。
A general rule is that joins are faster in most cases (99%). The more data tables have, the subqueries are slower. The less data tables have, the subqueries have equivalent speed as joins. The subqueries are simpler, easier to understand, and easier to read. Most of the web and app frameworks and their "ORM"s and "Active record"s generate queries with subqueries, because with subqueries are easier to split responsibility, maintain code, etc. For smaller web sites or apps subqueries are OK, but for larger web sites and apps you will often have to rewrite generated queries to join queries, especial if a query uses many subqueries in the query.
有人说“一些RDBMS可以将子查询重写为连接,或将连接重写为子查询,当它认为其中一个比另一个快时”,但这句话适用于简单的情况,当然不适用于带有子查询的复杂查询,这实际上会导致性能问题。
我认为在引用的答案中没有强调的是重复的问题和可能由特定(使用)案例引起的有问题的结果。
(尽管马塞洛·坎托斯提到过)
我将引用斯坦福大学Lagunita SQL课程的例子。
学生表
+------+--------+------+--------+
| 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 |
+------+--------+------+--------+
应用表
(向特定大学及专业申请)
+------+----------+----------------+----------+
| 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 |
+------+----------+----------------+----------+
让我们试着找出申请计算机科学专业的学生的平均绩点(不论大学)
使用子查询:
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 |
+------+
这个结果集的平均值是:
select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');
+--------------------+
| avg(GPA) |
+--------------------+
| 3.6800000000000006 |
+--------------------+
使用连接:
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 |
+------+
该结果集的平均值:
select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';
+-------------------+
| avg(GPA) |
+-------------------+
| 3.714285714285714 |
+-------------------+
It is obvious that the second attempt yields misleading results in our use case, given that it counts duplicates for the computation of the average value. It is also evident that usage of distinct with the join - based statement will not eliminate the problem, given that it will erroneously keep one out of three occurrences of the 3.9 score. The correct case is to account for TWO (2) occurrences of the 3.9 score given that we actually have TWO (2) students with that score that comply with our query criteria.
在某些情况下,除了性能问题,子查询似乎是最安全的方法。
MSDN文档SQL Server说
Many Transact-SQL statements that include subqueries can be alternatively formulated as joins. Other questions can be posed only with subqueries. In Transact-SQL, there is usually no performance difference between a statement that includes a subquery and a semantically equivalent version that does not. However, in some cases where existence must be checked, a join yields better performance. Otherwise, the nested query must be processed for each result of the outer query to ensure elimination of duplicates. In such cases, a join approach would yield better results.
所以如果你需要
select * from t1 where exists select * from t2 where t2.parent=t1.id
尝试使用join代替。在其他情况下,这没有什么区别。
我说:为子查询创建函数可以消除混乱的问题,并允许您为子查询实现额外的逻辑。因此,我建议尽可能为子查询创建函数。
代码中的混乱是一个大问题,几十年来业界一直在努力避免它。