【问题标题】:How to do bitwise exclusive OR in sql server between two binary types?如何在两种二进制类型之间在sql server中进行按位异或?
【发布时间】:2011-01-28 22:48:47
【问题描述】:

根据此链接: Bitwise Operators (Transact-SQL) 我们可以在 binary 和 int、smallint、tinyint 之间进行按位运算,反之亦然。

但是如何在 sql server 中对两种二进制类型进行按位异或? 或者,如果这不可能,我如何将二进制/varbinary 拆分为单个字节?

我要求这样做的原因是因为我需要对两个大于最大 int 值的数字进行异或运算。 谢谢。

【问题讨论】:

  • 试过但不起作用:“数据类型二进制和二进制在布尔异或运算符中不兼容。”
  • a ^ b 不等于(a | b) & !(a & b) 吗?还是我在这里玩得不亦乐乎?
  • 是的,但是 sql server 的按位限制适用于所有操作,而不仅仅是 XOR。请参阅问题中的链接。
  • 您是否已经看过 this thread(包括 Celko 在这个想法上的一些滥用和一些 UDF 建议)?
  • 它将上升到多少位/

标签: sql sql-server tsql


【解决方案1】:

代码块中的所有 cmets

-- variables
declare @vb1 binary(16), @vb2 binary(16), @lo binary(8), @hi binary(8)

-- 2 guids to compare
declare @guid1 uniqueidentifier set @guid1 = '96B4316D-1EA7-4CA3-8D50-FEE8047C1329'
declare @guid2 uniqueidentifier set @guid2 = 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF'

-- split every 8 bytes into a binary(8), which is a bigint, the largest size usable with XOR
select @vb1 = @guid1, @vb2 = @guid2

-- xor the high and low parts separately
select @hi = convert(binary(8), substring(@vb1,1,8)) ^ convert(bigint, substring(@vb2,1,8))
select @lo = convert(binary(8), substring(@vb1,9,8)) ^ convert(bigint, substring(@vb2,9,8))

-- the final result, concatenating the bytes using char(8) - binary -> uniqueidentifier
select 'A', @guid1 union all
select 'B', @guid2 union all
select 'A XOR B = ', convert(uniqueidentifier, convert(binary(16),convert(char(8),@hi) + convert(char(8),@lo)))

【讨论】:

  • 可能已经过时了,但在最后一行你可以将其简化为 select 'A XOR B = ', convert(uniqueidentifier, @hi + @lo)
【解决方案2】:

根据Bitwise Exclusive OR documentation

注意

只有一个表达式可以是 binary 或 varbinary 数据类型 按位运算。

【讨论】:

  • 你知道为什么会这样吗?我的猜测是返回变量的大小不确定......但它不能大于您传入的最大二进制文件。
【解决方案3】:

Martin 的问题中的评论让我知道了如何拆分二进制文件,以便我可以对这些值进行异或运算。 最初我想在 sql 中对两个 GUID 进行异或。所以这是我附带的代码:

declare @guid1 uniqueidentifier
declare @guid2 uniqueidentifier
declare @guid3_hi binary(8)
declare @guid3_lo binary(8)
declare @guid3_temp varchar(32)
declare @guid3_char varchar(36)
declare @guid3 uniqueidentifier

set @guid1 = '96B4316D-1EA7-4CA3-8D50-FEE8047C1329'
set @guid2 = 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF'

set @guid3_hi = CAST(SUBSTRING(CAST(@guid1 as binary(16)),1,8) as bigint) ^ CAST(SUBSTRING(CAST(@guid2 as binary(16)),1,8) as bigint)
set @guid3_lo = CAST(SUBSTRING(CAST(@guid1 as binary(16)),9,8) as bigint) ^ CAST(SUBSTRING(CAST(@guid2 as binary(16)),9,8) as bigint)

set @guid3_temp = SUBSTRING(dbo.sp_hexadecimal(@guid3_hi), 3, 16) + SUBSTRING(dbo.sp_hexadecimal(@guid3_lo), 3, 16)
select @guid3_temp

set @guid3_char = SUBSTRING(@guid3_temp, 1, 8) + '-' + SUBSTRING(@guid3_temp, 9, 4) + '-' + SUBSTRING(@guid3_temp, 13, 4) + '-' + SUBSTRING(@guid3_temp, 17, 4) + '-' + SUBSTRING(@guid3_temp, 21, 12)
select @guid3_char

set @guid3 = convert(uniqueidentifier, @guid3_char)
select @guid3

--result 92CE4B69-58E1-5CB3-72AF-0117FB83ECD6

二进制转十六进制字符串的函数来自:Converting Binary Data to Hexadecimal String

我知道在 SQL 2008 中我们可以使用转换函数来执行此操作,正如这篇文章所解释的:SQL Server 2008 : new binary – hex string conversion,但在我的情况下这不是一个选项。

但是,如果有人对我们如何对二进制数据类型使用 SQL 位运算有更好的了解,那就太好了。

编辑:

感谢cyberkiwi 提供正确的算法并指出我的代码中的错误。 此代码可能适用于 XOR-ing 二进制文件,但不适用于 GUID,因为 GUID 的前 8 个字节和后 8 个字节的字节顺序不同。请查看维基百科对此的解释:GUID Basic structure。请注意,如果您要将异或结果用作真正的 GUID,您应该考虑并考虑版本位。

【讨论】:

  • 这个结果是不是错了?每个字节的补码 (XOR) 应加起来为 F。9+9 > F 在第一个位置。比较最后 4 个字符 1329 + ECD6 = FFFF
  • @cyberkiwi 结果当然是错误的,感谢您指出。我只是忽略了结果,因为只查看了奇怪的是正确的最后 8 个字节。问题是将 uniqueidentifier 转换为二进制,它以某种方式没有正确转换前 8 个字节。了解为什么会发生这种情况以及我们如何正确地将 uniqueidentifier 转换为二进制会很有趣。
【解决方案4】:

我使用 bigints 来存储这两个值。这样,您将获得更大的范围。如果值大于 bigint,您可能需要将值拆分为两个 bigint 并使用 AND / OR 运算符组合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-07
    • 2011-08-09
    • 1970-01-01
    • 2014-06-19
    • 2019-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多