在SQL Server中,在一个重新设计的项目中,我走过一些旧的sprocs,我已经碰到了这一点。我希望在这个例子中抓住了本质:
SELECT * FROM People
Id | Name
-------------------------
1 | Bob Slydell
2 | Jim Halpert
3 | Pamela Landy
4 | Bob Wiley
5 | Jim Hawkins
SELECT a.*
FROM (
SELECT DISTINCT Id, Name
FROM People
WHERE Id > 3
) a
LEFT JOIN People b
ON a.Name = b.Name
WHERE b.Name IS NULL
请在此处忽略格式,样式和查询效率问题。这个例子仅仅是为了捕捉我正在使用的真实查询的确切本质。
在查看了真实的,更复杂的查询版本之后,我把它烧到了上面,我不能为我的生活看到它将如何返回任何数据。由于LEFT JOIN
检查,b.Name IS NULL
应始终排除刚刚选中的所有内容,对吧? (它是同一张桌子)。如果找到来自People的行,其中b.Name IS NULL会变为true,那么这不应该意味着找不到People a
中找到的数据吗? (不可能?)
为了清楚起见,我没有找到"解决方案"。代码就是这样。我只是试图了解它的行为,以便重新设计它。
如果这段代码确实永远不会返回结果,那么我会得出结论,它是错误地写的并且在重新设计过程中使用了这些知识。
如果有一个有效的数据场景可以/可以返回结果,那么这对我来说是新闻,我将不得不回到关于SQL连接的书籍! #DrivenCrazy
答案 0 :(得分:2)
是。在某些情况下,此查询将检索行。
查询
SELECT a.*
FROM (
SELECT DISTINCT Id, PName
FROM People
WHERE Id > 3
) a
LEFT JOIN People b
ON a.PName = b.PName
WHERE b.PName IS NULL;
大致(甚至可能完全)等同于......
select distinct Id, PName
from People
where Id > 3 and PName is null;
<强>为什么吗
使用此代码(mysql)测试它。
create table People (Id int, PName varchar(50));
insert into People (Id, Pname)
values (1, 'Bob Slydell'),
(2, 'Jim Halpert'),
(3,'Pamela Landy'),
(4,'Bob Wiley'),
(5,'Jim Hawkins');
insert into People (Id, PName) values (6,null);
现在运行查询。你得到了
6, Null
我不知道你的架构是否允许空名。
P.Name有什么值,a.PName = b.PName找不到匹配,b.PName是否为空?
好吧,它就在那里写的。 b.PName是Null。我们能否证明没有其他情况会返回行?
假设(Id,PName)有一个值,使得PName不为空并返回一行。
为了满足条件......
其中b.PName为空
...此类值必须包含与人员表中的任何PName都不匹配的PName。
所有a.PName和所有b.PName值均来自People.PName ...
所以a.PName可能与自己不匹配。
SQL中唯一不等于自身的标量值是Null。
因此,如果没有包含Null PName的行,则此查询不会返回行。
这是我提议的随意证明。
这是非常令人困惑的代码。所以#DrivenCrazy是合适的。
答案 1 :(得分:1)
查询的含义正是&#34;返回ID为&gt;的人3和null作为名称&#34;,即它可以返回数据但仅在名称中有空值时才会返回:
SELECT DISTINCT Id, PName
FROM People
WHERE Id > 3 and PName is null
如果我们考虑左连接条件... LEFT JOIN People b ON a.PName = b.PName
和(整体)条件where p.pname is null
的含义,则证明这一点非常简单:
通常情况下,条件where PName = PName
为真,当且仅当PName
不为空时,它与where PName is not null
具有完全相同的含义。因此,左连接仅匹配pname is not null
的元组,但任何匹配的行随后将被整体条件where pname is null
过滤掉。
因此,左连接不能在查询中引入任何新行,并且它不能减少左侧的行集(因为左连接从不这样做)。所以左连接是多余的,唯一有效的条件是where PName is null
。
答案 2 :(得分:1)
LEFT JOIN ON返回INNER JOIN ON返回的行加上左表的不匹配行,对于右表列,由NULL扩展。如果ON条件不允许匹配的行在某些列中具有NULL(如此b.NAME
等于某事),那么结果中该列中的唯一NULL来自不匹配的左手行。因此,保留该列的NULL作为结果,确切地给出了INNER JOIN ON无法匹配的行。 (这是一个成语。在某些情况下,它也可以通过NOT IN或EXCEPT表示。)
在您的情况下,左表有不同的People行a.Id > 3
,右表有所有People行。因此,a
中唯一不匹配的a.Name = b.Name
行是a.Name IS NULL
。所以WHERE返回那些由NULL扩展的行。
SELECT * FROM
(SELECT DISTINCT * FROM People WHERE Id > 3 AND Name IS NULL) a
LEFT JOIN People b ON 1=0;
但是你SELECT a.*
。所以整个查询只是
SELECT DISTINCT * FROM People WHERE Id > 3 AND Name IS NULL;
答案 3 :(得分:0)
sure.left join将返回数据,即使连接是在同一个表上完成的。 根据您的查询
"SELECT a.*
FROM (
SELECT DISTINCT Id, Name
FROM People
WHERE Id > 3
) a
LEFT JOIN People b
ON a.Name = b.Name
WHERE b.Name IS NULL"
由于最终过滤"b.Name IS NULL"
而返回null。如果没有过滤,它将返回2 records with id > 3