SQL左连接问题与空值

时间:2017-06-28 19:15:21

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

我继承了一个使用左连接的查询。查询正在执行的操作之一是删除任何已存档的记录,即存档=' 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&#39 ;.我认为将过滤器与"放在"上会出现问题。是它会返回一个记录,其中c.archived =' N'并在归档字段中放置一个null,这是不正确的:

    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为null的记录。

我只是想把归档的记录带回来&lt;&gt; &#39; Y&#39;,包括字段为空的那些。

要清楚,这就是表中记录的样子:

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

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

    Tom             56          NULL
    Rob             57          N

John将被淘汰,因为Archived =&#39; Y。&#39;

还有其他办法吗?

3 个答案:

答案 0 :(得分:0)

如果我理解正确,您希望在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值。

答案 1 :(得分:0)

我相信你正在寻找这个版本。您只需从拥有的内容开始,然后修改连接条件以覆盖不等于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)

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

WHERE P.commiteeeid = C.commiteeid

但它只是模仿INNER JOIN。

比较NULL的问题是您不能使用&lt;&gt;测试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)

如果你只有价值观&#39; Y&#39; &#39; N&#39;和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

答案 2 :(得分:-1)

解决方案取决于您希望如何处理左连接中的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)