通过WHERE子句连接的查询与使用实际JOIN的查询之间有什么实质性的区别吗?


32

在“ 学习SQL的艰辛方法”(练习6)中,作者提出了以下查询:

SELECT pet.id, pet.name, pet.age, pet.dead
    FROM pet, person_pet, person
    WHERE
    pet.id = person_pet.pet_id AND
    person_pet.person_id = person.id AND
    person.first_name = "Zed";

然后继续说:

实际上,还有其他一些使这类查询起作用的方法,称为“联接”。我现在避免使用这些概念,因为它们令人困惑。只需现在就采用这种连接表的方式,而忽略尝试告诉[您]这在某种程度上较慢或“低级”的人们。

真的吗?为什么或者为什么不?


3
我认为没有,但是您可以尝试做一个EXPLAIN来查看查询执行是否有任何区别。
GrandmasterB

6
我想在标题中用“艰难的方式”指出作品的矛盾信号,跳过“因为它们疯狂地令人困惑”的概念。但是也许我对“艰难的道路”应该是什么的看法是错误的。但是,也许不是。
Mindwin

7
JOIN很好地传输了意图(联接表),这将WHERE部分留给了实际的过滤器,并使它的读取更加容易。(除其他影响外)
00:00

2
如果作者不愿意编写简单的联接,则您正在学习SQL。正如ThomasS所说,通过使用JOIN可以使意图更清楚,并且WHERE子句变得更加简单。同样,使用JOIN可以更好地说明支持SQL的集合论。
Daniel Hollinrake 2015年

1
我不确定说“教您教什么”的感觉,但是“嘿,我们要跳过这个基本概念,因为它是craaazzzyyyyy香蕉。” 我想我最终会寻找其他来源来学习。在某些时候,您需要进行外部连接和交叉连接,并且应该知道如何进行连接。
莫里斯·里夫斯

Answers:


23

使用作者的方法,教授外部联接将变得更加困难。INNER JOIN中的ON子句从来没有像其他很多东西一样让我着迷。也许是因为我从来没有学过旧方法。我想认为我们摆脱它是有原因的,这并不是自鸣得意,而是将此方法称为低类。

在作者创建的非常狭窄的场景中确实如此:

  • 这样的入门级SQL使用ON很复杂
  • 仅考虑JOIN / INNER JOIN,不考虑任何外部JOIN
  • 孤立的编码员,无需阅读其他人的代码,也不需要任何有使用ON用法经验的人都可以阅读/使用他们的代码。
  • 不需要复杂的查询,其中包含很多:表,if,but和or。

作为教学进展的一部分,我认为将其分解并具有自然进展是比较容易的:

Select * from table
select this, something, that from table
select this from table where that = 'this'
select this from table join anothertable on this.id = that.thisid

联接和过滤表的概念并不完全相同。现在,学习正确的语法将在您学习OUTER JOINS时带来更多的影响,除非作者打算教一些过时/过时的东西,例如: *= or =*


5
之所以添加JOIN语句,是因为没有用于表示外部联接的标准,因此每个数据库供应商都有自己的“特殊”(不兼容)语法。IIRC Oracle具有*==*指示左或右外部联接,我使用的另一个仅使用|=运算符支持左外部联接。
TMN 2015年

1
@TMN IIRC甲骨文使用过+=或者也许是=+。我相信*=是Transact-SQL(Sybase和更高版本的MS-SQL)。不过,好点。
大卫

1
当您混合使用内部联接和外部联接时,它开始变得复杂(IMHO)。在这种情况下,我承认有时确实会退回到在WHERE子句中执行联接的“低级”技术。(我听说这被称为theta联接,但是我不确定这是否正确。)
David

IIRC运算符(例如“大于”或“等于”)有时被称为“ theta运算符”,但是Google搜索导致微积分中的某些运算。
Walter Mitty,2015年

12

它是否变慢取决于查询优化器及其简化查询的方式(您写的实际上不是执行的内容)。但是,此报价的最大问题是它完全忽略了存在不同类型的联接而它们的工作方式完全不同的事实。例如,所说的(理论上)对是正确的inner joins,但对outer joinsleft joinsright joins)不适用。


9
+1对于其他类型的联接。我的大多数加入都是INNER JOINLEFT OUTER JOIN。他们没有“疯狂地困惑”。SQL可能会令人困惑,但这不是一个例子。
mgw854

题外话,但要声明是不同类型的连接的小号加入的类型
user1451111

9

作者介绍了一个简单的案例,其中可以使用旧语法也可以使用新语法。我不同意他/她的说法,即连接令人发疯,因为连接表是基本的SQL查询概念。因此,也许作者应该花一些时间来解释JOINS的工作方式,然后再说出自己的观点以及做一个多表查询的例子。

一个应该使用较新的语法。主要参数是您的查询将具有:

  • 选择条件
  • 加盟条件
  • 筛选条件

使用旧样式,将合并和过滤条件组合在一起,在更复杂的情况下可能导致混淆。

另外,通过忽略filter子句中的联接条件,可以获得笛卡尔乘积:

 person_pet.person_id = person.id

使用较旧的语法。

使用较新的语法还指定了连接的发生方式,这对于您是否需要INNER,LEFT OUTER等非常重要。因此,对于JOIN语法而言更为明确,因为IMHO可以提高不熟悉连接表的人的可读性。


5

无论如何,查询解析器都应该为等效查询生成等效的内部表示形式。作者仅使用SQL-92之前的语法,这就是为什么他提到它可能被视为“过时的”或“低级的”。在内部,解析器和优化器应生成相同的查询计划。


5

我以这种方式学习了SQL,包括 *=外部联接语法。对我来说,这是非常直观的,因为所有关系都具有同等的优先权,并且在将查询设置为一系列问题方面做得更好:您想要什么?您想从哪里得到它们?您要哪一个?

通过执行join语法,它会更强烈地破坏对关系的思考过程。就个人而言,由于表和关系的混杂,我发现代码的可读性差得多。

至少在MSSQL中,假设您使用相同的联接顺序,查询的性能没有任何有意义的区别。也就是说,以这种方式学习(和使用)SQL 存在一个明显的巨大问题。如果您忘记了其中的一种关系,将会产生意想不到的交叉结果。在任何非平凡规模的数据库中,哪一个成本太高(对非选择者都是危险的!)。使用join样式语法时,忘记一个关系要困难得多。


7
这是一个关系数据库,因此关系对于查询非常重要。我个人发现,将混合了真实过滤器(foo.x = 5)和关系(foo.x = bar.x)的查询变得更加困难。引擎可以很容易地将其优化为联接,但是与集合和子集相反,人类必须逐行对其进行推理。
亚罗诺(Aaronaught)2015年

4

需要考虑两个不同方面: 性能可维护性/可读性

可维护性/可读性

我选择了一个不同的查询,因为我认为这是一个比您发布的原始查询更好/更差的示例。

什么对您来说看起来更好并且更具可读性?

select
    e.LoginID,
    DepartmentName = d.Name
from HumanResources.Employee e
inner join HumanResources.EmployeeDepartmentHistory edh
on e.BusinessEntityID = edh.BusinessEntityID
inner join HumanResources.Department d
on edh.DepartmentID = d.DepartmentID
where d.Name = 'Engineering';

要么...

select
    e.LoginID,
    DepartmentName = d.Name
from HumanResources.Employee e, 
HumanResources.EmployeeDepartmentHistory edh,
HumanResources.Department d
where e.BusinessEntityID = edh.BusinessEntityID
and edh.DepartmentID = d.DepartmentID
and d.Name = 'Engineering';

就我个人而言,第一个是可读性强的。您将看到我们使用来联接表INNER JOIN,这意味着我们将拉出与后续联接子句匹配的行(即“将Employee与BusinessEntityID上的EmployeeDepartmentHistory联接并包括那些行”)。

后者,逗号对我没有任何意义。这让我想知道您正在使用所有这些WHERE子句谓词。

前者读起来更像我的大脑所想。我每天都在看SQL,以及连接的逗号。这引出我的下一个观点...

实际上,还有其他方法可以使这类查询起作用,称为“联接”。

他们都是联接。连逗号都是联接。作者没有给他们打电话的事实确实是他们的失败....这不是显而易见的。应该很明显。您正在联接关系数据,无论您指定JOIN还是,

性能

这绝对是依赖RDBMS的。我只能代表Microsoft SQL Server发言。在性能方面,这些是等效的。你怎么知道的?捕获执行后的计划,并查看SQL Server对于以下每个语句的确切作用:

在此处输入图片说明

在上图中,我突出显示了我正在使用上述两个查询,只是在连接(JOINvs ,)的显式字符上有所不同。SQL Server完全一样。

摘要

不要使用逗号。使用显式JOIN语句。


我很早就学会了INNER JOINs,直到我意识到带有WHERE子句的变体是等效的,并且您的两个示例对我来说都非常可读。带有WHERE和逗号的代码可能更具可读性。我认为它落在大型复杂查询中,而不是这些相对简单的查询中。
罗伯特·哈维

重点是,认为逗号变体不是关系联接根本不正确。
Thomas Stringer

我认为您错误地将逗号解释为联接。逗号只是分开的表;创建联接的是WHERE条件,而不是逗号。
罗伯特·哈维

1
我可以肯定地说,谓词子句中没有任何连接。我认为您在错误地解释您的关系查询的构造。您是否尝试了不使用WHERE子句的逗号加入?它仍然有效。这是笛卡尔联接。您认为使用逗号有什么好处?请不要说您要保存字符。
托马斯·斯金格2015年

1
我想说第一个更好,因为您的意图更清晰。含糊不清的地方要少得多。
Daniel Hollinrake 2015年

4

不,那不是真的。作者正在引起读者的困惑,并鼓励不拘一格的编程,避免了标准语法与他喜欢的较旧版本之间非常强大的结构差异。具体来说,混乱的WHERE子句使找出他的查询特别的原因变得更加困难。

他的例子使读者产生了其含义的思维图,其中有很多混乱。

SELECT pet.id, pet.name, pet.age, pet.dead
    FROM pet, person_pet, person
    WHERE
    pet.id = person_pet.pet_id AND
    person_pet.person_id = person.id AND
    person.first_name = "Zed";

大致来说,上面是:

获取所有宠物,person_pet和宠物ID恰好与person_pet的pet_id匹配的人的宠物的ID,NAME,AGE和DEAD,并且该记录的person_id与FIRST_NAME为“ Zed”的人的person_id匹配。

有了这样的思维导图,读者(出于某种原因用手编写SQL的读者)很容易犯一个错误,可能是通过省略一个或多个表。而且,以这种方式编写的代码的读者将必须更加努力地工作,才能准确地了解SQL作者正在尝试执行的操作。(“ Harder”在具有或不具有语法突出显示的情况下处于读取SQL的级别,但仍大于零。)

加入JOIN的原因很常见,这是古老的经典“关注分离”标准。具体来说,对于SQL查询,有充分的理由区分数据的结构方式和数据过滤方式。

如果查询写得更整洁,例如

SELECT pet.id, pet.name, pet.age
FROM pet
  JOIN person_pet ON pet.id = person_pet.pet_id
  JOIN person ON person.id = person_pet.person_id
WHERE 
  person.first_name = "Zed";

然后,读者可以清楚地了解所要内容的各个组成部分。该查询的独特过滤器与其组件之间的相互关系是分开的,每个关系的必要组件紧挨着它们所需要的位置。


当然,任何现代数据库系统都不应在这两种样式之间看到有意义的区别。但是,如果仅考虑数据库性能,则SQL查询也不会有空格或大写字母。


2
既然我已经多次听到这种说法,那么让我扮演魔鬼的拥护者。艰难地学习X是关于具有技术深度;精通SQL的任何人都应该真正知道这两种方法在它们产生的输出方面是等效的
罗伯特·哈维

1
我可以看到这一点,但是作者并不仅仅是断言它们等同于体面的SQL Server的语句。他们声称使用JOIN是“令人困惑的”,这是脏代码等待的路径。(“不,不要使用LINQ,只需要手工编写FOR语句即可。”“编译器不在乎我所说的这种方法,因此没有理由不将其命名为FN1”)
DougM 2015年

3

Guy正在犯一个经典错误。他正在尝试讲授具有特定实现方式的抽象概念。一旦这样做,就会陷入这种混乱。

应该先教过基本的数据库概念,然后再将SQL作为描述它们的一种方式。

可以说左右联接并不重要。外连接,很好,您可以使用old *==*语法。

现在您可以争论语法更简单,但仅适用于简单查询。一旦开始尝试对此版本进行复杂的查询,您将陷入混乱。没有引入“新”语法,因此您可以执行复杂的查询,因此可以以可读且可维护的方式进行复杂的查询。


3
“艰难学习X”是一种不同的学习方法。您编写代码,然后再理解。
罗伯特·哈维

7
@RobertHarvey这不是一种不同的学习方法,它是标准的方法。仅当车轮脱落时您仍然保持在原位时才发生。有太多的人在写SQL时以为表格是单元格的矩形数组,以至于对此方法没有信心。
托尼·霍普金森

2

该示例等效于使用内部JOIN的简单重构。区别仅在于JOIN语法允许的其他可能性。例如,您可以指定处理涉及的两个表的列的顺序。参见例如https://stackoverflow.com/a/1018825/259310

当有疑问时,所接受的智慧就是以使查询更具可读性的方式编写查询。但是,JOIN或WHERE公式是否更易于阅读似乎是个人喜好的问题,这就是为什么两种形式都如此广泛的原因。


好的答案,尽管您是否WHEREJOIN语句中使用或放置子句实际上可能会对性能产生影响,具体取决于查询优化器。我已经看到它发生了不止一次。
洛克

我对性能影响的经验是:隐式联接将使查询优化器有更多选项来优化查询,这看似不错,但可能会成为问题。具体来说,查询优化器可以在开发中以一种方式在生产中调整查询。优化器可能会被愚弄以降低性能。我的建议是使用显式联接语法,并确认联接正在使用具有索引的列,以使性能可预测。
Michael Potter

2

当我学习SQL时,INNER JOIN,LEFT JOIN等表格不存在。正如其他答案已经指出的那样,SQL的不同方言都使用特殊语法实现了外部联接。这破坏了SQL代码的可移植性。将语言重新组合在一起需要进行一些更改,并且他们选择了LEFT JOIN等。

的确,对于每个INNER JOIN,都可以编写带有WHERE子句中的连接条件的等效逗号连接。我花了一段时间才从喜欢旧表格迁移到偏爱新表格。显然,《艰苦学习SQL》的作者仍然认为旧方法更容易。

有什么区别吗?好吧,是的。首先是带有ON子句的INNER JOIN比旧样式的连接更能清楚地表明作者的意图。ON子句实际上是联接条件而不是其他某种限制的事实更加明显。这使得使用INNER JOIN的代码在阅读时比旧样式更容易学习。在维护其他人的代码时,这一点很重要。

第二个区别是,新样式使查询优化器发现获胜策略的难度略微提高。这是很小的效果,但它是真实的。

第三个区别是,当您学习使用INNER JOIN(或只是简单的JOIN)时,它使学习LEFT JOIN等更加容易。

除此之外,根本没有实质性的区别。


0

这取决于您是否考虑集合和形式逻辑。

如果不这样做,则不使用“ join”关键字可使从形式逻辑到SQL的过程更加简单。

但是,如果像99%的人一样,您不喜欢数学学位的形式逻辑,那么join关键字会让您学习起来更容易。SQL过去曾在大学中作为记录形式逻辑查询的另一种方式出现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.