这会在同一个表上留下连接返回数据吗?

时间:2017-04-18 04:18:48

标签: sql sql-server

在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

4 个答案:

答案 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不为空并返回一行。

  1. 为了满足条件......

    其中b.PName为空

    ...此类值必须包含与人员表中的任何PName都不匹配的PName。

  2. 所有a.PName和所有b.PName值均来自People.PName ...

  3. 所以a.PName可能与自己不匹配。

  4. SQL中唯一不等于自身的标量值是Null。

  5. 因此,如果没有包含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