【问题标题】:Is there something wrong with joins that don't use the JOIN keyword in SQL or MySQL?在 SQL 或 MySQL 中不使用 JOIN 关键字的连接是否有问题?
【发布时间】:2010-09-12 20:08:49
【问题描述】:

当我开始编写数据库查询时,我还不知道 JOIN 关键字,自然我只是扩展了我已经知道的内容并编写了如下查询:

SELECT a.someRow, b.someRow 
FROM tableA AS a, tableB AS b 
WHERE a.ID=b.ID AND b.ID= $someVar

现在我知道这与 INNER JOIN 相同,我在我的代码中找到了所有这些查询,并问自己是否应该重写它们。它们有什么异味吗?还是很好?


我的回答总结:这个查询没有任何问题,但是使用关键字很可能会使代码更具可读性/可维护性。

我的结论:我不会改变我的旧查询,但我会更正我的写作风格并在未来使用关键字。

【问题讨论】:

  • ANSI SQL 标准随时间而变化。表达外连接的能力早于 JOIN 子句语法的引入。连接语法顺便为索引和连接提示提供了机会。
  • 你的总结是错误的,让你的结论很糟糕。查询的不同之处在于,下面的答案将在更小的数据子集上执行 where 子句。性能要好得多,因此您应该考虑在可能的情况下使用内部连接重写查询!
  • DanielSpiewak 接受的答案是错误的。他们幻想着实施。对 from 的非外部连接的优化,在 & where syntax 是微不足道的。 MySQL具体看官方文档rejoin optimization&其他方面的优化)。
  • 逗号是交叉连接,优先级低于关键字连接。仅此而已。

标签: sql mysql join


【解决方案1】:

在某些常见情况下,仅使用 WHERE 过滤连接可能效率极低。例如:

SELECT * FROM people p, companies c 
    WHERE p.companyID = c.id AND p.firstName = 'Daniel'

大多数数据库将直接执行此查询,首先获取peoplecompanies 表中的Cartesian product,然后然后 过滤匹配companyIDid 的那些字段。虽然完全无约束的乘积只存在于内存中且仅存在片刻,但它的计算确实需要一些时间。

更好的方法是在相关的情况下使用JOINs 对约束进行分组。这不仅在主观上更容易阅读,而且效率更高。因此:

SELECT * FROM people p JOIN companies c ON p.companyID = c.id
    WHERE p.firstName = 'Daniel'

有点长,但数据库可以查看ON 子句并使用它直接计算完全约束的JOIN,而不是从everything 开始然后限制下。这计算速度更快(尤其是对于大型数据集和/或多表连接)并且需要更少的内存。

我更改了我看到的每个使用“逗号JOIN”语法的查询。在我看来,它存在的唯一目的是简洁。考虑到性能影响,我认为这不是一个令人信服的理由。

【讨论】:

  • 您能否提供在旧连接语法上进行笛卡尔积的数据库系统示例?旧语法很常见,我认为大多数 DBMS 优化器都知道不这样做。
  • 我也坚持迈克尔写的东西。在 StackOverflow 中肯定需要 [引文需要]。
  • 请您的 DBMS 向您解释这两个语句。一个好的系统会让旧的 SQL 和 SQL 1992 的连接语法执行等效的操作。
  • 您说“大多数数据库”生成“笛卡尔积”。不是这样。许多关系数据库(DB2、Oracle、Sybase)早于 SQL-92 JOIN 语法。在此之前,所有连接谓词都在 WHERE 子句中。缺少连接谓词是导致笛卡尔积的原因。大多数数据库选择有效的访问路径,并且仅在该路径具有最低计算成本(或者如果它是唯一可用的访问路径,或者如果该语句被暗示)时选择“笛卡尔积”。 SQL-92 JOIN 语法的一大优势在于,语句中缺少连接谓词更加明显。
【解决方案2】:

更详细的INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN 来自用于连接的ANSI SQL/92 语法。对我来说,这种冗长的内容让开发人员/DBA 更清楚地了解连接的意图。

【讨论】:

  • 并且它可以更容易地发现缺少的连接谓词。 JOIN 语法使错误的语句看起来是错误的。在旧式语法中更难发现缺少的连接谓词。
【解决方案3】:

在 SQL Server 中总是有查询计划要检查,文本输出如下:

SET SHOWPLAN_ALL ON
GO

DECLARE @TABLE_A TABLE
(
    ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    Data VARCHAR(10) NOT NULL
)
INSERT INTO @TABLE_A
SELECT 'ABC' UNION 
SELECT 'DEF' UNION
SELECT 'GHI' UNION
SELECT 'JKL' 

DECLARE @TABLE_B TABLE
(
    ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    Data VARCHAR(10) NOT NULL
)
INSERT INTO @TABLE_B
SELECT 'ABC' UNION 
SELECT 'DEF' UNION
SELECT 'GHI' UNION
SELECT 'JKL' 

SELECT A.Data, B.Data
FROM
    @TABLE_A AS A, @TABLE_B AS B
WHERE
    A.ID = B.ID

SELECT A.Data, B.Data
FROM
    @TABLE_A AS A
    INNER JOIN @TABLE_B AS B ON A.ID = B.ID

现在我将省略表变量创建的计划,但两个查询的计划是相同的:

 SELECT A.Data, B.Data  FROM   @TABLE_A AS A, @TABLE_B AS B  WHERE   A.ID = B.ID
  |--Nested Loops(Inner Join, OUTER REFERENCES:([A].[ID]))
       |--Clustered Index Scan(OBJECT:(@TABLE_A AS [A]))
       |--Clustered Index Seek(OBJECT:(@TABLE_B AS [B]), SEEK:([B].[ID]=@TABLE_A.[ID] as [A].[ID]) ORDERED FORWARD)
 SELECT A.Data, B.Data  FROM   @TABLE_A AS A   INNER JOIN @TABLE_B AS B ON A.ID = B.ID
  |--Nested Loops(Inner Join, OUTER REFERENCES:([A].[ID]))
       |--Clustered Index Scan(OBJECT:(@TABLE_A AS [A]))
       |--Clustered Index Seek(OBJECT:(@TABLE_B AS [B]), SEEK:([B].[ID]=@TABLE_A.[ID] as [A].[ID]) ORDERED FORWARD)

所以,简短的回答 - 无需重写,除非您每次维护它们时都花很长时间尝试阅读它们?

【讨论】:

    【解决方案4】:

    这更像是一种语法选择。我更喜欢将我的连接条件与我的连接分组,因此我使用 INNER JOIN 语法

    SELECT a.someRow, b.someRow
    FROM tableA AS a
    INNER JOIN tableB AS b
      ON a.ID = b.ID
    WHERE b.ID = ?
    

    (?作为占位符)

    【讨论】:

      【解决方案5】:

      您的示例中的语法没有任何问题。 'INNER JOIN' 语法通常被称为 'ANSI' 语法,并出现在您的示例中说明的样式之后。它的存在是为了阐明连接的类型/方向/成分,但通常在功能上与您所拥有的没有什么不同。

      每个数据库平台都支持“ANSI”连接,但如今它或多或少是通用的。

      作为旁注,“ANSI”语法的一个补充是“FULL OUTER JOIN”或“FULL JOIN”。

      希望这会有所帮助。

      【讨论】:

        【解决方案6】:

        一般来说:

        使用 JOIN 关键字链接(即“加入”)主键和外键。

        使用 WHERE 子句将结果集限制为仅包含您感兴趣的记录。

        【讨论】:

          【解决方案7】:

          可能出现的一个问题是,当您尝试在同一查询中混合使用旧的“逗号样式”连接和 SQL-92 连接时,例如,如果您需要一个内连接和另一个外连接。

          SELECT *
          FROM table1 AS a, table2 AS b
           LEFT OUTER JOIN table3 AS c ON a.column1 = c.column1
          WHERE a.column2 = b.column2;
          

          问题是最近的 SQL 标准说 JOIN 在逗号连接之前进行评估。因此,在 ON 子句中对“a”的引用会产生错误,因为尚未定义相关名称,因为正在评估该 ON 子句。这是一个非常令人困惑的错误。

          解决方案是不要混合使用两种样式的连接。您可以在旧代码中继续使用逗号样式,但如果您编写新查询,请将所有联接转换为 SQL-92 样式。

          SELECT *
          FROM table1 AS a
           INNER JOIN table2 AS b ON a.column2 = b.column2
           LEFT OUTER JOIN table3 AS c ON a.column1 = c.column1;
          

          【讨论】:

            【解决方案8】:

            在旧的联接语法中要考虑的另一件事是,由于没有 on 子句,因此很容易意外获得笛卡尔联接。如果 Distinct 关键字在查询中并且它使用旧式连接,请将其转换为 ANSI 标准连接并查看是否仍需要 distinct。如果您以这种方式修复意外的笛卡尔连接,则可以通过重写以指定连接和连接字段来极大地提高性能。

            【讨论】:

              【解决方案9】:

              我避免隐式连接;当查询非常大时,它们会使代码难以破译

              通过显式连接和良好的格式设置,无需 cmets 即可使代码更具可读性和可理解性。

              【讨论】:

              • +1 是的。缺少的连接谓词更容易被发现。 JOIN语法让出错的SQL语句看起来不对,更明显是有问题。
              【解决方案10】:

              这还取决于您是以这种方式进行内连接还是外连接。例如,在 WHERE 子句(=* 和 *=)中用于外部连接的 MS SQL Server 语法可能会产生与 OUTER JOIN 语法不同的结果,并且在 SQL Server 2005 中不再支持 (http://msdn.microsoft.com/en-us/library/ms178653(SQL.90).aspx)。

              【讨论】:

                【解决方案11】:

                表演呢???

                事实上,性能是 RDBMS 中一个非常重要的问题。

                所以问题是 什么是最高效的...使用 JOIN 或在 WHERE 子句中加入表?

                因为优化器(或者他们在PG中说的planer...)普通做的很好,两个执行计划是一样的,所以执行查询时的性能是一样的...

                但魔鬼隐藏在一些细节中......

                所有优化器都有有限的时间或有限的工作量来找到最佳计划......当达到限制时,结果是所有计算计划中的最佳计划,而不是所有可能的计划中更好的计划!

                现在的问题是当我使用 WHERE 子句而不是 JOIN 来连接表时,我会浪费时间吗?

                答案是YES

                是的,因为关系引擎使用的关系代数只知道 JOIN 运算符,而不是 WHERE 子句中的伪连接。因此,优化器(实际上是解析器或 algrebriser)所做的第一件事就是重写查询……这会失去一些获得最佳计划的机会!

                在我漫长的 RDBMS 职业生涯中(40 年...),我曾两次遇到过这个问题

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-06-09
                  • 1970-01-01
                  • 2010-10-10
                  • 2021-09-04
                  • 2013-02-02
                  • 2013-03-31
                  相关资源
                  最近更新 更多