Sql server 使用NOT IN时出现空值问题

Sql server 使用NOT IN时出现空值问题,sql-server,Sql Server,我有一个查询,我试图删除所有记录,如果实践和实体的组合只出现一次,并且其中一个记录的状态为“已终止”或“已撤销”,那么我希望将其删除。如果机构/实体有多个记录,且其中至少有一个记录的状态为“非终止/撤销”,则我希望保留这些其他记录。我编写了一个查询来实现这一点,如下所示 但是,我遇到的问题是,查询正在删除空记录。我理解这是因为Exists/NOT IN子句,其中NULL(终止、撤回)的计算结果为UNKNOWN 我尝试以相反的方式编写查询,使用NOT EXISTS/in,但删除了数千条其他记录。有

我有一个查询,我试图删除所有记录,如果实践和实体的组合只出现一次,并且其中一个记录的状态为“已终止”或“已撤销”,那么我希望将其删除。如果机构/实体有多个记录,且其中至少有一个记录的状态为“非终止/撤销”,则我希望保留这些其他记录。我编写了一个查询来实现这一点,如下所示

但是,我遇到的问题是,查询正在删除空记录。我理解这是因为Exists/NOT IN子句,其中NULL(终止、撤回)的计算结果为UNKNOWN

我尝试以相反的方式编写查询,使用NOT EXISTS/in,但删除了数千条其他记录。有人知道在这种情况下如何包含空值吗

    declare @tab Table
    (practice  varchar(100),
    entity    varchar(100),
    assignedto varchar(100),
    statusname varchar(50)
     )
    insert into @tab values ('F&S', 'Sinai', 'AnnM', NULL);
    insert into @tab values ('F&S', 'Levin', 'AnnM', NULL);
    insert into @tab values ('F&S', 'Hopkins','AnnM', NULL);

    select *,
    ROW_NUMBER()over(partition by entity, statusname order by entity) as rn
    from @tab t1
    where exists(select *
                 from @tab t2
                 where t1.practice = t2.practice
                 and t1.entity= t2.entity
                 and t1.assignedto = t2.assignedto
                 and t2.statusname not in( 'Withdrawn', 'Terminated'));

您可以将
ISNULL
与伪值一起使用:

select *,
ROW_NUMBER()over(partition by entity, statusname order by entity) as rn
from @tab t1
where exists(select *
             from @tab t2
             where t1.practice = t2.practice
             and t1.entity= t2.entity
             and t1.assignedto = t2.assignedto
             and ISNULL(t2.statusname, '@@@') 
                 not in( 'Withdrawn', 'Terminated'));

您可以将
ISNULL
与伪值一起使用:

select *,
ROW_NUMBER()over(partition by entity, statusname order by entity) as rn
from @tab t1
where exists(select *
             from @tab t2
             where t1.practice = t2.practice
             and t1.entity= t2.entity
             and t1.assignedto = t2.assignedto
             and ISNULL(t2.statusname, '@@@') 
                 not in( 'Withdrawn', 'Terminated'));

我宁愿使用
t2。statusname为空
,所以优化器仍然可以使用任何索引

select *,
       ROW_NUMBER()over(partition by entity, statusname order by entity) as rn
from @tab t1
where exists(select 1
             from @tab t2
             where t1.practice = t2.practice
               and t1.entity= t2.entity
               and t1.assignedto = t2.assignedto
               and (t2.statusname IS NULL OR
                    t2.statusname not in ('Withdrawn', 'Terminated')
                   )
            );

我宁愿使用
t2.statusname为空
,所以优化器仍然可以使用任何索引

select *,
       ROW_NUMBER()over(partition by entity, statusname order by entity) as rn
from @tab t1
where exists(select 1
             from @tab t2
             where t1.practice = t2.practice
               and t1.entity= t2.entity
               and t1.assignedto = t2.assignedto
               and (t2.statusname IS NULL OR
                    t2.statusname not in ('Withdrawn', 'Terminated')
                   )
            );


最愚蠢的方式:
(t2.statusname不在('dracked','Terminated')或t2.statusname不为NULL)
@LONG这里没有最愚蠢的东西。应该是正确的方法。@JuanCarlosOropeza,哈哈,只是懒洋洋地使用
ISNULL
等,就像下面xD的答案一样,谢谢you@LONG您应该用
(…或…
括起来以避免混淆
其中
包含多个条件:)如果为null,则合并将起作用,但它们不会使用索引。你可以做和不做(t2.statusname in('deculled','Terminated'))最愚蠢的方式:
(t2.statusname不in('deculled','Terminated')或t2.statusname不为NULL)
@LONG这里没有最愚蠢的东西。应该是正确的方法。@JuanCarlosOropeza,哈哈,只是懒洋洋地使用
ISNULL
等,就像下面xD的答案一样,谢谢you@LONG您应该用
(…或…
括起来以避免混淆
其中
包含多个条件:)如果为null,则合并将起作用,但它们不会使用索引。您可以执行也可以不执行(t2.statusname in('deculled','Terminated'))
t2.statusname不为NULL更好。可以使用index@JuanCarlosOropeza一般来说是(SARGable计数:))。仍然在OP的示例中,有一个表变量,所以它不重要。请不要使用ISNULL<代码>不为空,状态名称不在
中可以利用任何索引<另一方面,code>ISNULL(…)将强制进行完整的表扫描,以便计算结果。表变量可以有主键和索引。我没有使用表变量。我刚刚在这里创建了一个,以便人们可以测试它。
t2.statusname不为NULL
更好。可以使用index@JuanCarlosOropeza一般来说是(SARGable计数:))。仍然在OP的示例中,有一个表变量,所以它不重要。请不要使用ISNULL<代码>不为空,状态名称不在中可以利用任何索引<另一方面,code>ISNULL(…)将强制进行完整的表扫描,以便计算结果。表变量可以有主键和索引。我没有使用表变量。我刚刚在这里创建了一个,这样人们就可以测试它了。而不是(t2.statusname in('retracted','Terminated'))将是同一代码的较短版本。这似乎可以工作,谢谢!比我想象的要简单得多。而不是(t2.statusname in('retracted','Terminated')将是同一代码的较短版本。这似乎可行,谢谢!比我想象的简单多了。