关于sql:Join与子查询

Join vs. sub-query

我是一个老门派的MySQL用户,总是喜欢使用JOIN而不是子查询。但是现在每个人都使用子查询,我讨厌它;我不知道为什么。

如果有什么不同,我缺乏自己判断的理论知识。子查询是否与JOIN一样好,因此没有什么可担心的?


子查询是解决"从A中获取事实,以B中的事实为条件"形式问题的逻辑正确方法。在这种情况下,将B插入子查询比进行联接更有逻辑意义。从实际意义上讲,它也更安全,因为您不必担心由于与B的多次比赛而从A中得到重复的事实。

但实际上,答案通常归结于表演。有些乐观主义者在给出一个连接与子查询时会吸吮柠檬,而有些乐观主义者则相反,这是乐观主义者特有的、DBMS版本特有的和查询特有的。

从历史上看,显式连接通常是成功的,因此已经确立的连接更好的智慧,但是乐观主义者一直都在变得更好,所以我更喜欢先以逻辑一致的方式编写查询,然后在性能约束允许的情况下重新构造查询。


在大多数情况下,JOIN比子查询更快,而且子查询更快是非常罕见的。

JOIN中,RDBMS可以创建一个更适合查询的执行计划,可以预测应该加载哪些数据以进行处理并节省时间,而不像子查询那样,它将运行所有查询并加载所有数据以进行处理。

子查询的好处在于,它们比JOIN的可读性更强:这就是大多数新的SQL人员更喜欢它们的原因;这是一种简单的方法;但在性能方面,连接在大多数情况下都更好,即使它们也不难读取。


摘自mysql手册(13.2.10.11将子查询重写为join):

A LEFT [OUTER] JOIN can be faster than an equivalent subquery because the server might be able to optimize it better—a fact that is not specific to MySQL Server alone.

所以子查询可能比LEFT [OUTER] JOIN慢,但在我看来,它们的强度稍微高一些。


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

PostgreSQL可以将子查询重写为join或join-to-subquery,前提是它认为一个比另一个快。这一切都取决于数据、索引、相关性、数据量、查询等。


首先,要比较这两个查询,首先应将查询与子查询区分为:

  • 一类子查询,始终使用联接编写相应的等效查询。
  • 无法使用联接重写的子查询类
  • 对于第一类查询,一个好的RDBMS将把联接和子查询视为等价的,并将生成相同的查询计划。

    现在连MySQL都这样做了。

    尽管如此,有时也不会,但这并不意味着连接总是会赢——我在MySQL中使用子查询时遇到过一些情况,这提高了性能。(例如,如果有什么东西阻碍了MySQL Planner正确估计成本,并且计划器没有看到连接变量和子查询变量相同,那么子查询可以通过强制某个路径来优于连接)。

    结论是,如果您想确定哪一个查询的性能更好,那么应该同时测试您的查询的联接和子查询变体。

    对于第二个类,比较没有意义,因为这些查询不能使用联接重写,在这些情况下,子查询是完成所需任务的自然方式,您不应该歧视它们。


    在2010年,我将加入这个问题的作者,并将强烈投票支持JOIN。但是有了更多的经验(尤其是在MySQL中),我可以声明:是的,子查询可以更好。我在这里读了很多答案。有人说,子查询速度更快,但缺乏很好的解释。我希望我能提供这个(非常)迟的答案:

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

    第二个重要声明:规模至关重要

    如果使用子查询,您应该知道DB服务器是如何执行子查询的。特别是如果子查询被计算一次或每行一次!另一方面,现代数据库服务器能够进行大量优化。在某些情况下,子查询有助于优化查询,但更新版本的DB服务器可能会使优化过时。

    选择字段中的子查询

    1
    SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo

    请注意,对于来自foo的每个结果行执行子查询。如果可能的话,避免这种情况,它可能会大大降低对大型数据集的查询速度。但如果子查询没有引用foo的话,数据库服务器可以将其作为静态内容进行优化,并且只能进行一次评估。

    WHERE语句中的子查询

    1
    SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

    如果幸运的话,数据库会在内部将其优化为一个JOIN。如果不这样做,您的查询在大型数据集上将变得非常非常缓慢,因为它将对foo中的每一行执行子查询,而不仅仅是像select类型那样的结果。

    join语句中的子查询

    1
    2
    3
    4
    5
    SELECT moo, bar
      FROM foo
        LEFT JOIN (
          SELECT MIN(bar), me FROM wilco GROUP BY me
        ) ON moo = me

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

    嵌套子查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT moo, bar
      FROM (
        SELECT moo, CONCAT(roger, wilco) AS bar
          FROM foo
          GROUP BY moo
          HAVING bar LIKE 'SpaceQ%'
      ) AS temp_foo
      GROUP BY bar
      ORDER BY bar

    可以在多个级别中嵌套子查询。如果必须对结果进行分组或排序,这可以帮助处理大型数据集。通常,DB服务器为此创建一个临时表,但有时您不需要对整个表进行排序,只需要对结果集进行排序。根据表的大小,这可能提供更好的性能。

    结论

    子查询不能代替JOIN,您不应该这样使用它们(尽管可能)。在我看来,正确使用子查询是用来快速替换CREATE TEMPORARY TABLE ...。一个好的子查询在某种程度上减少了一个数据集,你不能在一个JOINON语句中完成。如果子查询有一个关键字GROUP BYDISTINCT并且最好不位于select字段或where语句中,那么它可能会大大提高性能。


    SQL Server的msdn文档显示

    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.

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

    1
    SELECT * FROM t1 WHERE EXISTS SELECT * FROM t2 WHERE t2.parent=t1.id

    尝试改用join。在其他情况下,这没有区别。

    我说:为子查询创建函数可以消除混乱的问题,并允许您为子查询实现额外的逻辑。因此,我建议尽可能为子查询创建函数。

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


    我认为在引用的答案中被低估的是可能由特定(使用)案例引起的重复和有问题的结果问题。

    (尽管马塞洛·坎托斯确实提到过)

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

    学生桌

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    +------+--------+------+--------+
    | 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 |
    +------+--------+------+--------+

    应用表格

    (针对特定大学和专业的申请)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    +------+----------+----------------+----------+
    | 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        |
    +------+----------+----------------+----------+

    让我们试着找出申请过CS专业(不考虑大学)的学生的GPA分数。

    使用子查询:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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 |
    +------+

    此结果集的平均值为:

    1
    2
    3
    4
    5
    6
    7
    SELECT avg(GPA) FROM Student WHERE sID IN (SELECT sID FROM Apply WHERE major = 'CS');

    +--------------------+
    | avg(GPA)           |
    +--------------------+
    | 3.6800000000000006 |
    +--------------------+

    使用联接:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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 |
    +------+

    此结果集的平均值:

    1
    2
    3
    4
    5
    6
    7
    SELECT avg(GPA) FROM Student, Apply WHERE Student.sID = Apply.sID AND Apply.major = 'CS';

    +-------------------+
    | avg(GPA)          |
    +-------------------+
    | 3.714285714285714 |
    +-------------------+

    很明显,第二次尝试会在我们的用例中产生误导性的结果,因为在计算平均值时,它会计算重复项。很明显,在基于连接的语句中使用distinct并不能消除这个问题,因为它会错误地将3.9分数的三分之一保留下来。正确的情况是,假设我们有两(2)名学生的分数符合我们的查询条件,那么我们就要考虑两(2)次出现的3.9分。

    在某些情况下,除了性能问题外,子查询似乎是最安全的方式。


    从一个老的mambo cms运行在一个非常大的数据库上:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT id, alias
    FROM
      mos_categories
    WHERE
      id IN (
        SELECT
          DISTINCT catid
        FROM mos_content
      );

    0秒

    1
    2
    3
    4
    5
    6
    7
    SELECT
      DISTINCT mos_content.catid,
      mos_categories.alias
    FROM
      mos_content, mos_categories
    WHERE
      mos_content.catid = mos_categories.id;

    ~3秒

    一个解释表明,他们检查的行数完全相同,但一个需要3秒,一个接近于瞬间。故事的寓意?如果绩效很重要(不是什么时候?),尝试多种方法,看看哪种方法最快。

    还有…

    1
    2
    3
    4
    5
    6
    7
    SELECT
      DISTINCT mos_categories.id,
      mos_categories.alias
    FROM
      mos_content, mos_categories
    WHERE
      mos_content.catid = mos_categories.id;

    0秒

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


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

    1
       SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');

    请注意,对子查询的结果使用普通值运算符只需要返回一个字段。如果您有兴趣检查一组其他值中是否存在单个值,请使用:

    1
       SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');

    这显然不同于左联接,即您只想联接表A和B中的内容,即使联接条件在表B中找不到任何匹配的记录,等等。

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


    根据我的观察,就像两个例子,如果一个表的记录少于100000条,那么连接将很快工作。

    但是,如果一个表有超过100000条记录,那么子查询是最好的结果。

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

    1
    2
    3
    SELECT *
    FROM crv.workorder_details wd
    INNER JOIN  crv.workorder wr ON wr.workorder_id = wd.workorder_id;

    Result : 13.3 Seconds

    1
    2
    3
    SELECT *
    FROM crv.workorder_details
    WHERE workorder_id IN (SELECT workorder_id FROM crv.workorder)

    Result : 1.65 Seconds


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

    我也觉得join总是比mysql中的子查询好,但是explain是一种更好的判断方法。下面是一个例子,其中子查询比联接工作得更好。

    以下是我的3个子查询:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;

    解释显示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    +----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
    | 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 |
    +----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

    使用join的相同查询是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;

    输出为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    +----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
    | 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                     |
    +----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

    通过比较rows列可以看出不同之处,而带有连接的查询使用Using temporary; Using filesort

    当然,当我同时运行这两个查询时,第一个查询是在0.02秒内完成的,第二个查询即使在1分钟后也不会完成,所以请解释清楚这些查询。

    如果我在list_tag表上没有内部连接,即如果我删除

    1
    AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL

    从第一个查询开始,相应地:

    1
    INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

    在第二个查询中,explain为两个查询返回相同数量的行,并且这两个查询的运行速度相同。


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

    1
    2
    3
    4
    5
    SELECT titles, price
    FROM Books, Orders
    WHERE price =
    (SELECT MIN(price)
     FROM Orders) AND (Books.ID=Orders.ID);

    2)使用连接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;


    • 一般来说,连接在大多数情况下更快(99%)。
    • 数据表越多,子查询就越慢。
    • 数据表的数量越少,子查询的速度与联接速度相同。
    • 子查询更简单、更易于理解和阅读。
    • 大多数web和app框架及其"orm"和"active record"使用子查询生成查询,因为使用子查询更容易划分责任、维护代码等。
    • 对于较小的网站或应用程序,子查询是可以的,但是对于较大的网站和应用程序,您通常需要重写生成的查询来联接查询,特别是如果查询中使用了许多子查询。

    有人说"有些RDBMS认为一个子查询比另一个子查询快时,就可以将它重写为一个join或一个join到一个子查询。"但这句话适用于简单的情况,当然不适用于带有子查询的复杂查询,因为子查询实际上会导致性能问题。


    只有当第二个联接表的数据明显多于主表时,才会看到这种差异。我有过如下的经历…

    我们有一个用户表,有10万个条目,他们的成员数据(友谊)大约有30万个条目。这是一个连接语句,用来接收朋友和他们的数据,但是有很大的延迟。但在成员表中只有少量数据的情况下,它工作得很好。一旦我们将其更改为使用子查询,它就工作得很好。

    但同时,联接查询正在处理的其他表的条目数比主表少。

    所以我认为join和sub-query语句工作得很好,这取决于数据和情况。


    现在,许多DBS可以优化子查询和联接。因此,您只需使用explain检查查询,并查看哪个查询更快。如果在性能上没有太大的差异,我更喜欢使用子查询,因为它们简单易懂。


    我只是在想同样的问题,但是我在from部分使用了子查询。我需要从大表进行连接和查询,"从"表有2800万条记录,但结果只有128条那么小的结果大数据!我正在对它使用max()函数。

    首先,我使用左连接,因为我认为这是正确的方法,MySQL可以优化等等。第二次只是为了测试,我重写以针对连接进行子选择。

    左连接运行时:1.12s子选择运行时间:0.06s

    比加入快18倍!就在"乔基托"副词中,副词看起来很糟糕,但结果…


    如果要使用join加快查询速度:

    对于"内部联接/联接",不要使用Where条件,而是在"on"条件下使用它。如:

    1
    2
    3
    4
    5
    6
    7
    8
         SELECT id,name FROM table1 a  
       JOIN table2 b ON a.name=b.name
       WHERE id='123'

     Try,

        SELECT id,name FROM table1 a  
       JOIN table2 b ON a.name=b.name AND a.id='123'

    对于"左/右连接",不要在"on"条件下使用,因为如果使用左/右联接,它将获取任何一个表的所有行。因此,在"on"中不使用它。所以,尝试使用"Where"条件