最近我开始研究 SQL ServerANSI_Padding
设置。阅读 BOL 和一些在线文章后,我发现自己比开始时有更多的问题和困惑。
我知道我最好坚持使用,并且我了解当设置更改为或 时与和列ANSI_Padding On
的不同行为。NULL
NOT NULL
ON
OFF
但我无法理解这个概念。填充的目的是什么?向二进制数附加零(或向字符串附加空格)不会改变值吗?
二进制数0x0000ee等于0x00ee(至少转换为十进制时是这样)但是0x00ee怎么和00
0x00ee一样呢?
字符串“a”(1 个空格)与“a”(无空格)不同,但它们在同一列中都变成“a”(3 个空格)char(3)
。这怎么能被接受呢?
当值被修剪时也存在同样的混乱。
BOL 似乎确实提到它ANSI_Padding
用于控制值的存储方式。所以我最初的猜测是实际值没有改变。但BOL并没有详细阐述这一点,目前我找到的任何文章也没有详细阐述这一点。
感谢您的帮助。
首先 -这是最近的一篇文章,展示了事情如何运作得很好。
还有一篇来自 Microsoft 的文章,解释了当字符串具有尾随空格时 SQL Server 如何处理比较。
为什么要有衬垫?对于NOT NULL
所有数据都非常接近相同长度的列,将所有内容填充到(例如)10 个字符可能会比增加使列可变长度的开销消耗更少的空间。我认为使列NULL
能够需要与使其可变长度相同的开销,因此包括填充成为可选的。
当您提到 BOL 是在谈论值的存储方式时 - 是的,这确实会改变值。如果您在一列中存储'a'
(无空格)和'a '
(一个空格)char(3) NOT NULL
,它们都将作为'a
'(两个空格)检索。在char(3) NULL
列中,它们将被检索为'a'
或'a '
,具体取决于创建表(或列)时的 ANSI_PADDING 设置。
为什么尾部空格的丢失或保留看起来如此不重要?
基本上,当比较两个字符串时,SQL 会用空格填充较短的字符串,直到它们的长度相同。所以:
SELECT CASE WHEN 'a' = 'a ' THEN 'TRUE' ELSE 'FALSE' END as String_Comp;
Run Code Online (Sandbox Code Playgroud)
返回TRUE
。
另外,binary()
也许varbinary()
不应该将 和 视为数字的简单二进制表示(当然,它们是),而应将其视为二进制字符串。在将二进制值与二进制值进行比较时,SQL 不注意尾随零。
例如:0X00EE00 是十进制数 60928。0X00EE 是十进制数 238。
SELECT CASE WHEN CAST(0x00EE00 as int) = CAST(0x00EE as int)
THEN 'TRUE'
ELSE 'FALSE'
END as Binary_Number_Comp;
Run Code Online (Sandbox Code Playgroud)
返回FALSE
(60928 <> 238)。然而:
SELECT CASE WHEN CAST(0x00EE00 as binary(10)) = CAST(0x00EE as varbinary(6))
THEN 'TRUE'
ELSE 'FALSE'
END as Binary_String_Comp;
Run Code Online (Sandbox Code Playgroud)
返回TRUE
;正如 Max Vernon 所指出的,尾随零的作用就像字符串中的 NULL 终止符,并且不包含在比较中。
有趣的是:
SELECT CASE WHEN 0x00EE00 = 0x00EE THEN 'TRUE' ELSE 'FALSE' END as Binary_Value_Comp;
Run Code Online (Sandbox Code Playgroud)
返回TRUE
,因此默认情况下,这是将值作为二进制字符串而不是二进制数字进行比较。
根据上面的文档,LIKE
比较是根据尾随零区分字符串值的唯一方法。
SELECT CASE WHEN 'a' LIKE 'a ' THEN 'TRUE' ELSE 'FALSE' END as LIKE_String_Comp;
Run Code Online (Sandbox Code Playgroud)
返回FALSE
。
所以,总而言之,除非你真的在努力,否则无论 ANSI_PADDING 设置如何,SQL Server 都会忽略尾随空格,这就是为什么修剪或不修剪它们被如此漫不经心地对待。
顺便说一下,这里有一个简单的例子来说明ANSI_PADDING
询问:
SET ANSI_PADDING ON;
IF (OBJECT_ID('tempdb..#test') IS NOT NULL) DROP TABLE #test;
CREATE TABLE #test
( my_char CHAR(10) NOT NULL
,my_varchar VARCHAR(10) NOT NULL
,my_char_NULL CHAR(10) NULL
,my_binary BINARY(6) NOT NULL
,my_varbinary VARBINARY(6) NOT NULL
,my_binary_NULL BINARY(6) NULL
);
INSERT INTO #test
VALUES ('ABC ','ABC ','ABC ',0xABC000,0xABC000,0xABC000);
PRINT 'ANSI_PADDING ON:';
SELECT '|' + my_char + '|' as my_char
,'|' + my_varchar + '|' as my_varchar
,'|' + my_char_NULL + '|' as my_char_NULL
,my_binary
,my_varbinary
,my_binary_NULL
FROM #test;
SET ANSI_PADDING OFF;
IF (OBJECT_ID('tempdb..#test2') IS NOT NULL) DROP TABLE #test2;
CREATE TABLE #test2
( my_char CHAR(10) NOT NULL
,my_varchar VARCHAR(10) NOT NULL
,my_char_NULL CHAR(10) NULL
,my_binary BINARY(6) NOT NULL
,my_varbinary VARBINARY(6) NOT NULL
,my_binary_NULL BINARY(6) NULL
);
INSERT INTO #test2
VALUES ('ABC ','ABC ','ABC ',0xABC000,0xABC000,0xABC000);
PRINT 'ANSI_PADDING OFF:';
SELECT '|' + my_char + '|' as my_char
,'|' + my_varchar + '|' as my_varchar
,'|' + my_char_NULL + '|' as my_char_NULL
,my_binary
,my_varbinary
,my_binary_NULL
FROM #test2;
Run Code Online (Sandbox Code Playgroud)
结果:
ANSI_PADDING ON:
my_char my_varchar my_char_NULL my_binary my_varbinary my_binary_NULL
------------ ------------ ------------ -------------- -------------- --------------
|ABC | |ABC | |ABC | 0xABC000000000 0xABC000 0xABC000000000
ANSI_PADDING OFF:
my_char my_varchar my_char_NULL my_binary my_varbinary my_binary_NULL
------------ ------------ ------------ -------------- -------------- --------------
|ABC | |ABC| |ABC| 0xABC000000000 0xABC0 0xABC0
Run Code Online (Sandbox Code Playgroud)
注意: 正如 Max Vernon 在对其答案的评论中指出的那样,我上面的注释都是关于 SQL Server 如何处理事情的。无论从 SQL Server 检索数据的任何应用程序中的值相等,其行为都可能完全不同。