42

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

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

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


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

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

4

11 回答 11

38

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

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

大多数数据库将直接执行此查询,首先获取and表的笛卡尔积然后通过具有匹配和字段的那些进行过滤。虽然完全无约束的乘积只存在于内存中,并且只存在片刻,但它的计算确实需要一些时间。peoplecompaniescompanyIDid

更好的方法是将约束与JOIN相关的 s 分组。这不仅在主观上更容易阅读,而且效率更高。因此:

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

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

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

于 2008-09-24T19:43:40.067 回答
10

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

于 2008-09-24T18:41:26.067 回答
6

在 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)

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

于 2008-09-24T18:51:31.067 回答
5

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

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

(?作为占位符)

于 2008-09-24T18:41:18.937 回答
4

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

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

附带说明一下,“ANSI”语法的一个新增功能是“FULL OUTER JOIN”或“FULL JOIN”。

希望这可以帮助。

于 2008-09-24T18:40:27.630 回答
4

一般来说:

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

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

于 2008-09-24T19:46:40.327 回答
4

可能出现的一个问题是,当您尝试在同一查询中将旧的“逗号样式”联接与 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;
于 2008-09-24T19:49:26.247 回答
4

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

于 2009-02-17T22:00:42.137 回答
3

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

使用显式连接和良好的格式,代码在不需要注释的情况下更具可读性和可理解性。

于 2008-09-24T18:40:51.560 回答
2

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

于 2008-09-24T18:59:30.393 回答
0

表演呢???

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

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

因为优化器(或PG中所说的planer ...)普通的做得很好,两个执行计划是相同的,所以执行查询时的性能将是相同的......

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

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

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

答案是肯定的!

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

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

于 2021-11-10T09:48:46.267 回答