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

Posted

技术标签:

【中文标题】将所有内容都转换为 varbinary 是比较字符数据的正确方法吗?【英文标题】:Is converting everything to varbinary the correct way to compare character data? 【发布时间】:2013-05-19 02:02:37 【问题描述】:

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

    确定 sys.indexes 中的行数:

    select count(*) from sys.indexes;
    

    运行此 SQL:

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

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

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

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

    将“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;
    

    注意#5 中 MIN 函数的结果(0x7e 而不是 0x63)。

问题:

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

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

[已编辑...]

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

【问题讨论】:

忽略警告。它只是作为信息消息存在。您不应强制转换为 varbinary 来比较字符数据。 COUNT(col) 仅计算 NOT NULL 值。使用COUNT(*) 计算行数。我看不出这与比较字符数据有什么关系,你能告诉我们你最初遇到的问题吗? 那么它返回了什么,您期望什么以及您为什么决定转换为 varbinary 会解决它? 那么,在您开始沿着这条奇怪的转换为 varbinary 路径之前,您能否演示一个比较字符数据导致“缺少行的问题”的案例? 如果有人简单地回答是或否,则应删除该问题。 【参考方案1】:

已编辑和未删除

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

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

我认为这段代码 sn-p 回答了 count with 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;

即使将 NULL 替换为 char(0x7E),此查询也会显示我的排序规则 (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

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

【讨论】:

Answer A 将改变语义。 SELECT MIN(ISNULL(name,'')) from (SELECT 'Foo' UNION ALL SELECT NULL) T(name) 返回 '',即使它在数据中不存在。 嗯,是的,你是对的。我想我真的不明白 OP 试图做什么。 原始帖子中的每个问题“B”,我试图让 MIN 返回预期结果,因为在#6 中的 SQL 中(以及其他使用我尝试过的各种比较运算符的 SQL ), 它不是。尝试将 MIN 函数添加到答案中的第二个和第三个 SQL 语句(有或没有子字符串和/或 varbinary,或使用其他比较函数/运算符)。当计数正确时,比较错误,反之亦然。 您在编号 5 中的查询是询问表中所有记录的计数(在将 NULL 转换为非 NULL 后准确)以及替换任何 NULL 名称后所有名称记录的第一个字符记录~。如果您在 SQL 中对这些记录进行排序,波浪号 (0x7e) 是第一个,因此您的第二个 substring(min(coalesce([...]) 返回 ~。您的查询写得非常奇怪,但返回 exactly 正确的结果。它与比较字符数据无关。比较什么?您的查询只是对其进行排序,然后采用 MIN()。 我更新了答案。不同之处在于排序规则的排序顺序,我已经为它添加了一个测试。【参考方案2】:

我假设您不能使用 ISNULL 执行以下操作是有原因的:ISNULL(MyField,'Some String I will know is a null')

附言在生产环境中对大型数据集执行此操作时要小心,具体取决于您在做什么。

【讨论】:

这与已删除的答案相同,因为这会更改查询的语义。不应将两个为 NULL 的值转换为 some string,因为无法假设这两个 NULL应该相等。这仅适用于NULLsome string 表示相同的情况,这并不常见。 使用 ISNULL 代替 COALESCE 产生相同的结果: select count(isnull(name,char(0x7e))), substring(cast(min(isnull(name,char(0x7e))) as varbinary ),1,1) 来自 sys.indexes;

以上是关于将所有内容都转换为 varbinary 是比较字符数据的正确方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 varbinary 数据(作为字符串)转换为字节数组?

将字符串变量转换为 GUID

将 UTF-8 varbinary(max) 转换为 varchar(max)

sql数字字符串排序

将字节数组转换为字符串在c#中不起作用

PhpMyAdmin 正在将 varbinary 导出为奇怪的字符而不是 HEX