【问题标题】:SQL left join issue with null values空值的 SQL 左连接问题
【发布时间】:2017-06-28 19:15:21
【问题描述】:

我继承了一个使用左连接的查询。查询正在做的一件事是删除所有归档记录,即归档 = 'Y'。这是查询的样子:

    select P.firstname, E.entityid, C.committeeid, L.locationname
    from Pract P
    left join Committee C
    on P.commiteeid = C.committeeid
    and c.archived = 'N'
    left join Entity E
    on P.entityid = E.EntityID
    and e.archived = 'N'
    left join Location L
    on E.location = L.location
    and l.archived = 'N'

结果应该只返回归档 'Y' 的记录。我认为将过滤器设置为“on”的一个问题是它会返回一条记录,其中 c.archived = 'N' 并在归档字段中放置一个空值,这是不正确的:

    FirstName    EntityID     CommitteeId
    John            55         null

如果 c.archived = 'Y' 则记录不应显示。

我认为归档的过滤器应该在 where 子句中,像这样:

    select firstname, entityid, committeeid
    from Pract P
    left join Committee C
    on P.commiteeid = C.committeeid
    left join Entity E
    on P.entityid = E.entityid
    left join Location L
    on E.Locationid = L.locationid
    where c.archived = 'N'
    and e.archived = 'N'
    and l.archived = 'N'

我发现的问题是,在某些情况下,委员会的存档字段为空(不是“Y”或“N”)。使用我的解决方案错误地消除了自 null 'N' 以来的记录。

如果我试试这个:

   where c.archived <> 'Y'

它不起作用,我猜是因为 NULL 不会评估任何东西。

如果我试试这个:

   where (c.archived = 'N' or c.archived is null) 

它不起作用,因为它现在带回了由左连接引起的那些空记录。我不能用内连接替换左连接,因为这将排除 c.committeeid 为空的记录。

我只想带回归档'Y'的记录,包括那些字段为空的记录。

明确地说,表中的记录如下所示:

    FirstName    EntityID   Archived
     John           55          Y
     Tom            56          NULL
     Rob            57          N

在这种情况下,我希望返回的记录如下所示:

    Tom             56          NULL
    Rob             57          N

John 将被淘汰,因为 Archived = 'Y'。

还有其他方法吗?

【问题讨论】:

  • “我认为将过滤器设置为“on”的一个问题是它会返回一条记录,其中 c.archived = 'N' 并在归档字段中放置一个空值,这是不正确的。” 它完全按照它应该做的。您正在进行 left join 这意味着 left 表中的所有元素都应包含在结果中。由于在符合该过滤器的右表上找不到匹配项,因此您将获得这些列的空值。如果您只想要 right 表中匹配的元素,请执行 inner join
  • 我继承了这段代码,并试图让它正常工作。我相信原始开发人员没有以正确的方式编写此内容。如果“archived = 'N' 所以左连接不合适,我不希望返回记录。但如果表中的“归档”字段为 Null,我确实希望返回记录。我不能进行内部连接,因为有连接右侧表中的字段为空的实例。内部连接将消除这些。如果 C.committeeid 为空,我不能执行“on P.commiteeid = C.committeeid”。我只需要它返回记录“存档'Y'.
  • 您能给我们展示示例数据和预期结果吗?我不清楚您要达到的目标。
  • @jackstraw22 我的解决方案将适用于您的示例数据。

标签: sql sql-server-2008 left-join


【解决方案1】:

如果我理解正确,您想使用where 子句中的另一个 字段进行过滤:

select firstname, entityid, committeeid  -- this will return an error on committeeid
from Pract P left join
     Committee C
     on P.commiteeid = C.committeeid and
        (c.archived <> 'Y' or c.archived is null)
where c.committeeid is not null;

您也可以使用exists 更轻松地做到这一点:

select p.*
from Pract P
where not exists (select 1
                  from Committee C
                  where P.commiteeid = C.committeeid and
                        c.archived = 'Y'
                 );

这会自动处理NULL 值。

【讨论】:

  • 我的查询实际上有大约 15 个左连接并从每个表中提取字段。我对其进行了更新,以通过其中一些连接提供更准确的表示。
【解决方案2】:

我相信您正在寻找这个版本。您只需从您拥有的内容开始并修改连接条件以覆盖不等于 Y 且为 NULL 的值。

select firstname, entityid, committeeid
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
and (c.archived <> 'Y' OR c.archived IS NULL)

然后,如果您只需要在委员会表中有记录的项目。你把它放在 WHERE 子句中。

WHERE P.commiteeeid = C.commiteeid

但它只会模拟 INNER JOIN。

您比较 NULL 的问题是您不能使用 来测试 NULL 值。如果您想要具有 NULL 值的记录,则必须显式测试 IS NULL

所以您更新后的查询将如下所示:

select P.firstname, E.entityid, C.committeeid, L.locationname
from Pract P
left join Committee C
on P.commiteeid = C.committeeid
and (c.archived <> 'Y' OR c.archived IS NULL)
left join Entity E
on P.entityid = E.EntityID
and (e.archived <> 'Y' OR e.archived IS NULL)
left join Location L
on E.location = L.location
and (l.archived <> 'Y' OR l.archived IS NULL)

如果你只有值 'Y' 'N' 和 NULL,那么你也可以使用

and (c.archived = 'N' OR c.archived IS NULL)

为了确保我们相互理解,这里只是您查询的最小示例

create table Pract (firstname varchar(20), cid int)
create table comt ( cid int, archived varchar(1) )

insert into Pract values ('Tom',1),('Adam',2),('Mark',3),('Bob',4)
insert into comt values (1,'Y'), (2,'N'), (3,NULL)

--

select firstname, P.cid, C.cid, C.archived
from Pract P
LEFT join comt C
on P.cid = C.cid
and (c.archived <> 'Y' OR c.archived IS NULL)

有结果

  firstname cid cid archived
1     Tom   1   NULL    NULL
2     Adam  2   2       N
3     Mark  3   3       NULL
4     Bob   4   NULL    NULL

或使用 INNER JOIN

select firstname, P.cid, C.cid, C.archived
from Pract P
INNER join comt C
on P.cid = C.cid
and (c.archived <> 'Y' OR c.archived IS NULL)

有结果

  firstname cid cid archived
1   Adam    2   2   N
2   Mark    3   3   NULL

【讨论】:

  • 只有当归档值 = 'Y' 时,我才需要删除记录。如果委员会表中的存档值为 NULL,那么我需要它返回一个空值。 INNER JOIN 将删除此实例中的整个记录​​。
  • 好吧,我希望我终于做对了。仅当归档 Y 包含 NULL 值时,您才需要来自实践和来自委员会的所有内容。这是 this 应该返回的。
  • @Marek——是的。我再次更新了问题以显示需要返回的内容。
  • 如果委员会表中没有匹配项,它也会返回Tom NULL NULL。否则你需要 INNER JOIN。
  • @Marek--我需要左连接,因为有时没有匹配。如果在该实例中字段为 NULL,则很好。我只想删除 archived = 'Y' 的记录。
【解决方案3】:

解决方案取决于您希望如何处理左连接中的 NULL 值。如果 NULL 表示未归档,则

select firstname, entityid, committeeid
    from Pract P
    left join Committee C
    on P.commiteeid = C.committeeid
    left join Entity E
    on P.entityid = E.entityid
    left join Location L
    on E.Locationid = L.locationid
    where (c.archived = 'N' OR c.archived IS null)
    and (e.archived = 'N' OR e.archived IS null)
    and (l.archived = 'N' OR l.archived IS NULL)

【讨论】:

  • 这会将左连接更改为内连接,因此不推荐使用。
  • @HLGEM 你在说什么?这如何更改为内部连接
  • 我在我给出的例子中试过这个。此方法消除归档字段为 NULL 的记录。
  • @jackstraw22 立即尝试
  • 我最初尝试了 (c.archived = 'N' or c.archived is null),它从左连接中带回了空记录。
猜你喜欢
  • 1970-01-01
  • 2011-09-25
  • 2014-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-12
  • 2020-11-05
相关资源
最近更新 更多