我尝试创建表'MyTable'并添加具有小int类型的字段“ field1”。
然后我在字段1中添加负数-12289。
然后执行以下SQL查询:
select field1 ^ 0xcfff from MyTable
结果为零。
但是如果我用十六进制整数常量替换十六进制整数常量,如下所示:
select field1 ^ 53247 from MyTable
结果为-65536。
为什么?
大于2,147,483,647的整数常量将转换为十进制数据类型,而不是bigint数据类型。
但是0xcfff和53247都比2,147,483,647小得多。为什么它们会产生差异结果?
更新:
据我所知,这个问题的关键是我们可以将0xcfff强制转换为小整数,如下所示:
select cast(0xcfff as smallint)
但是我们不能将53247强制转换为小整数,以下行将导致溢出:
select cast(53247 as smallint)
这与C / C ++不同。在C / C ++中,两个强制类型转换都可以。
这是由于您的数据长度。执行按位运算符时,应使用相同的字节长度:
1100 1111 1111 1111 <-- -12289 as smallint (word)
1100 1111 1111 1111 <-- CFFF (-12289) (word)
0000 0000 0000 0000 <-- XOR result = 0 (word)
1111 1111 1111 1111 1100 1111 1111 1111 <-- -12289 as int (double word)
0000 0000 0000 0000 1100 1111 1111 1111 <-- 53247 (0000CFFF) (double word)
1111 1111 1111 1111 0000 0000 0000 0000 <-- XOR result = -65536 (double word)
您可以尝试更改长度:
select cast(-12289 as int) ^ 0x00cfff, -12289 ^ cast(0x00cfff as int)
您在这里缺少的鲜为人知的位是Data Type Precedence。这是检查方法:
declare @t table (
Id smallint not null
);
insert into @t (Id)
select -12289;
select sq.*,
sql_variant_property(sq.XBin, 'BaseType') as [BinType],
sql_variant_property(sq.XDec, 'BaseType') as [DecType]
from (
select t.Id,
t.Id ^ 0xcfff as [XBin],
t.Id ^ 53247 as [XDec]
from @t t
) sq;
二进制文字0xcfff
占用2个字节,因此可以隐式转换为列本身具有的smallint
类型。但是,十进制文字被解释为int
(不是因为它需要两个以上的字节,而是因为2 ^ 32-1下的SQL Server always interprets整数文字具有此数据类型,而所有更大的interpreted都将作为decimal
)。这意味着现在必须将该列隐式转换为优先级高于int
的smallint
,并且其符号在转换期间得以保留。