爲何不要建立將ANSI_PADDING設置爲OFF的列?

SQL Prompt是一款實用的SQL語法提示工具。它根據數據庫的對象名稱、語法和代碼片斷自動進行檢索,爲用戶提供合適的代碼選擇。自動腳本設置使代碼簡單易讀--當開發者不大熟悉腳本時尤爲有用。本文介紹了不要建立將ANSI_PADDING設置爲OFF的列。數據庫

查找永久更改表中某些數據類型檢索方式的鏈接設置,這有點奇怪,可是若是在建立臨時或永久表時使用SET ANSI_PADDING OFF,就會發生這種狀況。在建立表時,因爲設置不當,或者偶然使用帶有DBLib鏈接的舊應用程序,這會致使表中的某些列從那時起奇怪地或不一致地處理某些字符串或二進制數據類型的尾隨空格。ide

此選項已被棄用,在某些時候,它將被刪除(它將始終爲「on」)。SQL Prompt具備不同意使用的語法規則DEP013,它將警告您使用此選項以及其餘不同意使用的SET選項。函數

SQL Prompt使用教程:爲何不要建立將ANSI_PADDING設置爲OFF的列?

什麼是ANSI填充?爲何?工具

在SQL的早期,如何處理字符串的問題引發了爭議。定義和固定字符串長度的CHAR數據類型旨在使數據檢索簡單而有效。字符串以指定的長度存儲在CHAR數據類型中。對於較短的字符串,數據類型中全部剩餘的字符位置都用空格(CHAR)或零(BINARY)填充。這些填充空格是字符串的一部分嗎?若是不是,您如何區分故意填充空格的值和自動填充的值?post

SQL的早期實現一般在檢索數據時修剪掉CHAR中的全部尾隨空格,除非該列是NOT NULL。可是,很明顯,爲了符合ANSI SQL標準,必須對此進行更改。NIST測試套件檢查是否始終填充CHAR數據類型,而且對於CHAR或VARCHAR數據類型,用戶輸入的尾隨空格都不會被截斷。SQL Server決定,爲了遵照規則,將隨數據一塊兒檢索任何尾隨空格(不管是故意仍是做爲填充自動添加的),對於二進制數據類型的尾隨零也是如此。可是,因爲在舊的體制下編寫了太多代碼,所以Transact-SQL中引入了一種稱爲ANSI_PADDING的設置。當它關閉時,它容許此舊代碼照常工做。彷佛每一個人都很高興。測試

一旦有關CHAR數據類型的ANSI-ISO標準爭議平息下來,就會引入新的數據類型和新的表類型。ANSI_PADDING爭議僅影響當時存在的類型,而用戶爲此目的定義的長度。如今能夠將字符串存儲爲NVARCHAR、VARCHAR、NCHAR或CHAR的定義大小。二進制數據能夠存儲爲BINARY或VARBINARY的定義大小。對於CHAR(n)、BINARY(n)、VARCHAR(n)或VARBINARY(n)的較早數據類型,在建立表時ANSI_PADDING選項的設置會影響SQL Server隨後處理這些字符串的方式。spa

可是,後來的NCHAR、NVARCHAR、NTEXT、TEXT或IMAGE數據類型並不是如此。未定義長度的類型VARBINARY(MAX)、VARCHAR(MAX)和NVARCHAR(MAX)也不受影響。3d

舊數據庫開發人員真正須要關閉ANSI填充的惟一用途是無需使用該RTRIM()功能便可進行字符串鏈接。避免必須使用RTRIM()函數彷佛是個好主意,可是填充規則的行爲與有可空列的行爲不一致。另外,隨着新類型的表的引入,沒有人願意使它們向後兼容,所以適用的規則一般在ANSI_PADDING關閉時對錶變量根本不起做用。一樣,若是您嘗試在計算列或索引視圖上建立或更改索引,則極可能會陷入困境。若是您將ANSI_PADDING設置爲OFF,則根本不容許這樣作。orm

那麼,規則是什麼?對象

ANSI標準的簡單行爲是,對於固定寬度類型插入的數據,老是用尾隨空格或零填充到指定長度,而後,對於全部數據類型,任何尾隨空格或零都被視爲數據的一部分,依此類推。當SQL Server將數據檢索到內存時,將永遠不會修剪它們。

若是在建立表和列時將ANSI_PADDING切換爲OFF,則行爲將變得更加複雜。幸運的是,是否存在尾隨空格不會影響WHERE子句中的字符串比較,由於不管設置什麼,這些始終會忽略它們。它也不會對比較產生很大的影響。關閉ANSI_PADDING的主要效果以下:

  • CHAR NOT NULL和BINARY NOT NULL列在插入數據時被填充,而且隨後未進行修剪(與ANSI標準相同的行爲)

  • 在檢索時會修剪可空的CHAR和BINARY列(所以,固然在插入時再也不填充)。您會丟失任何尾隨空格或故意添加的零

  • 檢索時會修剪VARBINARY和VARCHAR列,所以您會丟失任何尾隨空格或故意添加的零

若是您須要說服力,咱們能夠證實全部這些。

PRINT 'Creating a temporary table with ANSI_PADDING ON';
SET ANSI_PADDING ON;
SET NOCOUNT ON;
CREATE TABLE #OnAnsiPaddingTest
  (
  TenCharsNull CHAR(10) NULL,
  TenCharsNotNull CHAR(10) NOT NULL,
  TenVarcharNull VARCHAR(10) NULL,
  TenVarcharNotNull VARCHAR(10) NOT NULL,
  TenVarbinaryNull VARBINARY(10) NULL,
  TenVarbinaryNotNull VARBINARY(10) NOT NULL
  );
<a id="post-1115931-_Hlk33089691"></a>PRINT 'Now creating identical temp table with ANSI_PADDING OFF';
SET ANSI_PADDING OFF;
SET NOCOUNT ON;
CREATE TABLE #OffAnsiPaddingTest
  (
  TenCharsNull CHAR(10) NULL,
  TenCharsNotNull CHAR(10) NOT NULL,
  TenVarcharNull VARCHAR(10) NULL,
  TenVarcharNotNull VARCHAR(10) NOT NULL,
  TenVarbinaryNull VARBINARY(10) NULL,
  TenVarbinaryNotNull VARBINARY(10) NOT NULL
  );
PRINT 'Now creating identical table variable with ANSI_PADDING OFF';
DECLARE @OffAnsiPaddingTest table
  (
  TenCharsNull CHAR(10) NULL,
  TenCharsNotNull CHAR(10) NOT NULL,
  TenVarcharNull VARCHAR(10) NULL,
  TenVarcharNotNull VARCHAR(10) NOT NULL,
  TenVarbinaryNull VARBINARY(10) NULL,
  TenVarbinaryNotNull VARBINARY(10) NOT NULL
  );
PRINT 'Switching ANSI_PADDING back on'
SET ANSI_PADDING ON;
PRINT 'inserting into both tables'
INSERT INTO #OffAnsiPaddingTest
  (TenCharsNull, TenCharsNotNull, TenVarcharNull, TenVarcharNotNull,
TenVarbinaryNull, TenVarbinaryNotNull)
VALUES
  ('First      ', 'Second    ', 'Third     ', 'fourth    ', 0x1234560000,
0x1234560000), --padded to 10
  ('First', 'Second', 'Third', 'fourth', 0x123456, 0x123456); --no trailing padding
INSERT INTO #OnAnsiPaddingTest
  (TenCharsNull, TenCharsNotNull, TenVarcharNull, TenVarcharNotNull,
TenVarbinaryNull, TenVarbinaryNotNull)
VALUES
  ('First      ', 'Second    ', 'Third     ', 'fourth    ', 0x1234560000,
0x1234560000), --padded to 10
  ('First', 'Second', 'Third', 'fourth', 0x123456, 0x123456); --no trailing padding
INSERT INTO @OffAnsiPaddingTest
  (TenCharsNull, TenCharsNotNull, TenVarcharNull, TenVarcharNotNull,
TenVarbinaryNull, TenVarbinaryNotNull)
VALUES
  ('First      ', 'Second    ', 'Third     ', 'fourth    ', 0x1234560000,
0x1234560000), --padded to 10
  ('First', 'Second', 'Third', 'fourth', 0x123456, 0x123456); --no trailing padding
PRINT 'Selecting from first table, created with ANSI padding ON (<> shows extent of string)'
SELECT '<' + Coalesce(TenCharsNull, '') + '> <' + TenCharsNotNull + '> <'
       + Coalesce(TenVarcharNull, '') + '> <' + TenVarcharNotNull + '> <'
       + Coalesce(Convert(VARCHAR(MAX), TenVarbinaryNull, 2), 'null') + '> <'
       + Convert(VARCHAR(MAX), TenVarbinaryNotNull, 2) + '>' AS AnsiPaddingOn
  FROM #OnAnsiPaddingTest AS APT;
PRINT 'Selecting from second table, created with ANSI padding OFF: Same query'
SELECT '<' + Coalesce(TenCharsNull, 'null') + '> <' + TenCharsNotNull + '> <'
       + Coalesce(TenVarcharNull, 'null') + '> <' + TenVarcharNotNull + '> <'
       + Coalesce(Convert(VARCHAR(MAX), TenVarbinaryNull, 2), 'null') + '> <'
       + Convert(VARCHAR(MAX), TenVarbinaryNotNull, 2) + '>' AS AnsiPaddingOff
  FROM #OffAnsiPaddingTest AS APT;
PRINT 'Selecting from table variable, created with ANSI padding OFF: Same query'
SELECT '<' + Coalesce(TenCharsNull, 'null') + '> <' + TenCharsNotNull + '> <'
       + Coalesce(TenVarcharNull, 'null') + '> <' + TenVarcharNotNull + '> <'
       + Coalesce(Convert(VARCHAR(MAX), TenVarbinaryNull, 2), 'null') + '> <'
       + Convert(VARCHAR(MAX), TenVarbinaryNotNull, 2) + '>' AS AnsiPaddingOff
  FROM @OffAnsiPaddingTest AS APT;
  DROP TABLE #OnAnsiPaddingTest;
DROP TABLE #OffAnsiPaddingTest;

您會看到如下消息:

使用ANSI_PADDING ON建立一個臨時表
如今使用ANSI_PADDING OFF建立相同的臨時表
如今使用ANSI_PADDING OFF'建立相同的表變量
從新打開ANSI_PADDING
插入兩個表
從第一個表中選擇,並在ANSI填充爲ON的狀況下建立(<>顯示字符串的範圍)
從第二個表中選擇,使用ANSI填充OFF建立:同一查詢
從表變量中選擇,使用ANSI填充OFF建立:相同的查詢

結果是這樣的:

SQL Prompt使用教程:爲何不要建立將ANSI_PADDING設置爲OFF的列?

正確的。與往常同樣,在建立表時將ANSI_PADDING設置爲ON,咱們故意添加尾隨空格或零的第一行就不會被裁剪。沒有尾隨空格的第二行被一致地添加爲CHAR和BINARY數據類型,不管是否容許NULL。

第二個結果來自關閉ANSI_PADDING時建立的表。可爲空的第一個CHAR列已被修剪。具備NOT NULL約束的CHAR列用空格填充。不管是否能夠爲空,VARBINARY列都修剪了尾隨零。VARCHAR列修剪了尾隨空格。

第三個結果來自一個表變量,該變量也是經過將ANSI_PADDING設置爲OFF來建立的,該設置徹底無害。不管設置如何,它的行爲都與ANSI兼容。

若是您正在努力接受全部規則和例外,那麼您並不孤單。

查找使用ANSI_PADDING關閉建立的異常列

在訪問表時,不管您對ANSI_PADDING進行了何種設置,查詢行爲都是一致的。該設置將保留在表列中,而鏈接設置將被忽略。不管使用何種鏈接設置訪問「舊版」數據庫,該數據庫都能始終如一地運行。咱們能夠經過查詢元數據來檢查臨時表發生了什麼。

USE tempdb
SELECT S.name AS TheColumn,
  Object_Schema_Name(S.object_id) + '.' + Object_Name(S.object_id) AS TableName,
  is_ansi_padded
  FROM sys.columns AS S
    INNER JOIN sys.tables AS t
      ON t.object_id = S.object_id
  WHERE system_type_id IN (165, 167, 173, 175) 
     AND is_ansi_padded = 0; --ansi padding off!!

SQL Prompt使用教程:爲何不要建立將ANSI_PADDING設置爲OFF的列?

sys.columns視圖中的列若是ANSI_PADDING處於打開狀態,則爲1;若是關閉,則爲0。該查詢將很是快速地告訴您數據庫是否有設置爲ANSI_PADDING off的異常列(只需去掉第一行「USE tempdb」)。

結論

除非有人最終有意或無心關閉ANSI_PADDING的危險消失,不然在從SQL Server中最終刪除該功能以前,請始終在執行表CREATE語句以前使用SET ANSI_PADDING ON設置與ANSI行爲的鏈接,但在其餘任何地方都不該使用的設置,由於該設置和支持已計劃棄用,此時您將沒法關閉ANSI兼容性。

相關文章
相關標籤/搜索