在SQL或者MySQL中不使用JOIN关键字的联接有问题吗?
当我开始编写数据库查询时,我还不知道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标准联接,然后查看我们是否仍需要该联接。如果以这种方式修复意外的笛卡尔联接,则可以通过重写以指定联接和联接字段来极大地提高性能。