-5

我使用 MIN 函数来比较允许空值的列中的字符数据,结果是灾难性的。;-) 这是一个非常简化的示例,它显示了相同的内容:

  1. 确定 sys.indexes 中的行数:

    select count(*) from sys.indexes;
    
  2. 运行此 SQL:

    select count(name), substring(cast(min(name) as varbinary),1,1) 
      from sys.indexes;
    

    如果 count 与 #1 匹配,请在此处停止。请尝试使用不同的数据库(包含更多对象)。

  3. 转到消息选项卡。您应该看到: 警告:空值被聚合或其他 SET 操作消除。

  4. 您将如何处理“名称”列中的空值以解决该警告?与合并?

  5. 将“name”替换为“coalesce(name,char(0x7e))”并运行 SQL:

    select count(coalesce(name,char(0x7e))), 
      substring(cast(min(coalesce(name,char(0x7e))) as varbinary),1,1) 
      from sys.indexes;
    
  6. 注意#5 中 MIN 函数的结果(0x7e 而不是 0x63)。

问题:

A. 合并是否是处理每个 #4 的警告和缺失(空)数据的正确方法?

B. 由于#6 中的结果不是预期的,那么在 SQL Server 中比较字符数据的正确方法是什么?将所有内容转换为 varbinary?

[已编辑...]

在下面的讨论中,对于通过 COALESCE 进行的空替换与比较结果之间的关系存在一些混淆和讨论。两者之间的关系是这样的:当您选择一个字符串(包括单个字符)作为空替换占位符时(上面的步骤 #4 和 #5),该字符串必须满足正在比较的预期结果针对查询中其他数据的值执行。使用某些归类,找到合适的字符串可能比使用其他归类更困难。

4

2 回答 2

2

已编辑和未删除

回答 A.:是的,或者在这种情况下,您可以使用与 COALESCE() 相同的结果的 ISNULL()。

对 B 的回答:不要将 varchar 转换为 varbinary 来比较它们,但要了解使用聚合时的排序规则排序顺序。

我认为这个代码片段回答了 NULL 问题的计数,但我仍然对这个问题有点困惑:

select count(*) from sys.indexes; 
-- 697 results
go
select count(isnull(name,'')) from sys.indexes; 
-- 697 results
go
select count(name) from sys.indexes; 
-- 567 results
go

这将获取 MINname字段的记录数(基于字符串字段的排序规则和 SQL 排序顺序):

select  i.name
        ,subCnt.Cnt
from    (select min(name) as name from sys.indexes) as i
join    (select name, count(*) as Cnt from sys.indexes group by name) as subCnt
on      subCnt.name = i.name;

此查询解释了聚合排序顺序以及上述查询为什么选择name字段中返回的值:

select name, row_number() over (order by name) from sys.indexes order by name;

即使用 char(0x7E) 替换 NULL,此查询也会显示我的排序规则 (Latin1_General_BIN) 的排序顺序:

select  coalesce(name,char(0x7e))
        , row_number() over (order by coalesce(name,char(0x7e))) 
from    sys.indexes order by 2;

这显示了 SQL Server 中排序规则之间的排序顺序差异(确定字符串字段中的 MIN 或 MAX):

declare @test table (oneChar char(1) collate Latin1_General_BIN
                    , oneChar2 char(1) collate SQL_Latin1_General_CP1_CI_AS
                    , varb varbinary)

insert into @test (oneChar)
select 'c' union all
select '~' union all
select 'P' union all
select 'X' union all
select 'q' union all
select NULL

update @test set varb = cast(isnull(oneChar,char(0x7E)) as varbinary), oneChar2 = oneChar

select min(oneChar) from @test -- 'P'
select min(oneChar2) from @test -- '~'
select min(varb) from @test  -- 0x50, the varbinary equivalent of oneChar

如果您想要所有行的计数并且想要名称的 MIN() 而不考虑 NULL(并且无论出于何种原因都没有看到警告),请使用以下命令:

select  i1.Cnt
        ,i2.name 
from    (select count(*) as Cnt from sys.indexes) as i1
        ,(select min(name) as name from sys.indexes where name is not null) as i2

不管你做什么,当然不要为了做一些过滤而将整个字段作为不同的排序规则。这个问题属于讨论论坛,而不是简单的问题/答案。

于 2013-05-23T16:03:17.400 回答
0

我假设你不能使用 ISNULL 做一些事情是有原因的: ISNULL(MyField,'Some String I will know is a null')

ps 请注意在生产环境中的大型数据集上的性能,具体取决于您在做什么。

于 2013-05-23T16:22:06.727 回答