将所有内容转换为varbinary是比较字符数据的正确方法吗?

时间:2013-05-23 15:54:26

标签: sql-server collation coalesce

我使用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;
    

    如果计数与#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)。

  7. 问题:

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

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

    [编辑...]

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

2 个答案:

答案 0 :(得分: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

这将获取MIN name字段的记录数(基于字符串字段的排序规则和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

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

答案 1 :(得分:0)

我假设有一个原因你不能使用ISNULL做的事情:ISNULL(MyField,'我会知道的某些字符串是空')

P.S。在生产环境中对大型数据集的性能要小心,具体取决于您正在做什么。