在SQL或者MySQL中不使用JOIN关键字的联接有问题吗?

时间:2020-03-06 14:40:50  来源:igfitidea点击:

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

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

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

编辑:

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

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

感谢回答!

解决方案

示例语法没有错。 " INNER JOIN"语法通常称为" ANSI"语法,并且紧随示例中的样式之后。它的存在是为了阐明联接的类型/方向/组成部分,但通常在功能上与我们所拥有的没有区别。

每个数据库平台都支持" ANSI"联接,但如今它们或者多或者少地变得普遍。

附带说明一下,带有" ANSI"语法的一个添加项是" FULL OUTER JOIN"或者" FULL JOIN"。

希望这可以帮助。

我避免隐式联接;当查询很大时,它们会使代码难以解读

借助显式联接和良好的格式,无需注释即可使代码更具可读性和可理解性。

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

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

(?是占位符)

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

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

因此,简短的答案无需重写,除非我们在每次维护它们时都花很长时间尝试阅读它们?

这也取决于我们是仅以这种方式执行内部联接还是外部联接。例如,WHERE子句中的外部联接的MS SQL Server语法(= =)可以提供与OUTER JOIN语法不同的结果,并且不再受支持(http://msdn.microsoft.com/zh-cn SQL Server 2005中的/library/ms178653(SQL.90).aspx)。

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

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

大多数数据库将完全按字面意义执行此查询,首先获取" people"和" companies"表的笛卡尔积,然后按具有" companyID"和" id"字段匹配的表进行过滤。尽管完全不受约束的产品并不存在于任何地方,而是存在于内存中,然后只存在片刻,但其计算确实需要一些时间。

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

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

它稍长一些,但是数据库能够查看" ON"子句,并使用它直接计算完全受限的" JOIN",而不是从所有内容开始然后进行限制。这样可以更快地进行计算(尤其是对于大型数据集和/或者多表联接),并且需要更少的内存。

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

一般来说:

使用JOIN关键字链接(即"联接")主键和外键。

使用WHERE子句将结果集限制为仅对我们感兴趣的记录。

可能出现的一个问题是,当我们尝试在同一查询中混合使用旧的"逗号样式"联接和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;

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