今天特地查了一下SQL Server下的校檢函數有哪些。原本我只是在工作中用過一個CHECKSUM,今天特地學習了一下才發現原來還有其他的校檢函數。
這里找到了別人對於SQL SERVER下這幾個校檢函數的學習總結,借此機會學習下別人的學習成果
http://bbs.51cto.com/thread-1145105-1.html
CHECKSUM和BINARY_CHECKSUM
- CHECKSUM和BINARY_CHECKSUM都是可以針對表中一行的單列或者多列又或是表達式生成數據類型為INT的校檢值。
- 不同的地方是BINARY_CHECKSUM是轉成了二進制后生成的校檢值。
- 並不是所有的數據類型都可以用到CHECKSUM或BINARY_CHECKSUM上的。BINARY_CHECKSUM 在計算中忽略具有不可比數據類型的列。 不可比數據類型包括 text、ntext、image、cursor、xml 和不可比公共語言運行庫 (CLR) 用戶定義的類型。
- MSDN上講到BINARY_CHECKSUM 可用於檢測表中行的更改。但是也提到BINARY_CHECKSUM(*) 將為大多數(但不是全部)行更改返回不同的值,並可用於檢測大多數行修改。
- 因為校檢值是一個INT,根據INT的數值分布[-2147483648,2147483647],如果某長表中的行數大於2億估計就會出現重復的情況了。這點在以前工作中就碰到過。
- CHECKSUM和BINARY_CHECKSUM的不同是:1)CHECKSUM是不區分大小寫。它認為Jerry和jerry是同樣的校檢值;2)如果兩個表達式具有相同的類型和字節表示,那么對於 BINARY_CHECKSUM 將返回相同的值。例如,BINARY_CHECKSUM 對於“2Volvo Director 20”和“3Volvo Director 30”將會返回相同的值。這段參考了http://ultrasql.blog.51cto.com/9591438/1607407
CHECKSUM_AGG
這個是個聚合函數。返回組中各值的校驗和。 將忽略 Null 值。 后面可以跟隨 OVER 子句。CHECKSUM_AGG 用於檢測表中的更改。 如果表達式列表中的某個值發生更改,則列表的校驗和通常也會更改。 但只在極少數情況下,校驗和會保持不變。很多情況下這個函數應該是用來檢測表的數據是否有改動或者表的某個字段的數據是否有發生改動。
HASHBYTES
HASHBYTES是一個加密算法函數,用來對數據進行加密、或校驗。它會返回其在 SQL Server 中的輸入的 MD2、MD4、MD5、SHA、SHA1 或 SHA2 哈希值。它可以是作為加密功能和強度上更加強大的函數。可以用MD5替換CHECKSUM或BINARY_CHECKSUM避免重復。但是性能上可能有所下降。當然了,比起SHA-256這種加密強度極強的算法,MD5還是完全能接受的。
---------------------------------------------------- Update on 11/21/2015 ---------------------------------------------------------
這些函數的一個作用就是用於數據校驗。舉一個非常常用的場景 -- 哈希索引。在一些數據倉庫(ETL)系統或者一些做EDI的系統里面,source傳一些文件給到我們,我們再對這些文件進行處理(加載)。這當中就需要隊列表。每次線程掃描目錄的時候要能夠知道文件是已經在隊列表里面,不需要再進一次隊列。那么我們就必然需要對“文件完整路徑”和“修改時間”建立一條索引來加快每次檢索的速度。而我們都知道SQL Server對索引的長度是有限制,最長不得超過16個字段或者900個字節長度(詳細參考MSDN的文章)。像“文件完整路徑”這種值肯定存在超過900個字節長度的可能性。解決辦法就是對“文件完整路徑”和“修改時間”建立一條哈希索引,因為哈希索引的長度即便是最長是SHA-256也不過64個字節,相比普通索引還是小得多的。需要說明,長度越長的哈希索引在加密過程所需要消耗的CPU越多,但是它會產生的哈希值沖突的可能性就越低。這個需要你根據對表具體的數據行增長和作為索引字段的個數的情況而選擇合適的檢驗函數。
但是有一點還是需要肯定的是,像上面這樣的例子盡量是不要選擇CHECKSUM,原因是CHECKSUM返回的是一個INT類型的值。我們都知道INT支持的最大值不過是2億多。這里做一個測試,在10萬行的時候其實已經出現了哈希值沖突的情況了。這種情況選擇MD5或者SHA-128/SHA-256。
USE [JerryDB] GO IF OBJECT_ID('dbo.ChecksumCollision') IS NOT NULL DROP TABLE dbo.ChecksumCollision GO CREATE TABLE dbo.ChecksumCollision(col1 NVARCHAR(4000), checksum_col AS CHECKSUM(col1)) GO INSERT dbo.ChecksumCollision(col1) SELECT CAST(CHECKSUM(NEWID()) AS VARCHAR(100)) FROM [dbo].[Numbers] WHERE [Num] <= 100000 select * from ( select col1, checksum_col, count(checksum_col) over (partition by checksum_col) as cnt from dbo.ChecksumCollision) t where cnt > 1 order by checksum_col
上面代碼的運行結果
下面測試測試一下用HASHBYTES('MD5',XXX)的情況,
USE [JerryDB] GO IF OBJECT_ID('dbo.ChecksumCollision') IS NOT NULL DROP TABLE dbo.ChecksumCollision GO CREATE TABLE dbo.ChecksumCollision(col1 NVARCHAR(4000), checksum_col AS HASHBYTES('MD5',col1)) GO INSERT dbo.ChecksumCollision(col1) SELECT CAST(CHECKSUM(NEWID()) AS VARCHAR(100)) FROM [dbo].[Numbers] WHERE [Num] <= 1000000 select * from dbo.ChecksumCollision where checksum_col in ( select checksum_col from dbo.ChecksumCollision group by checksum_col having count(distinct col1)>1) order by checksum_col
上代碼返回的結果
接下來來實現上面說的那個檢查文件隊列的例子
接下來我們建立一條索引
create index ix_ChecksumCollision_checksum_col on dbo.ChecksumCollision(checksum_col);
然后找一行來測試
然后試下運行下面的代碼利用哈希索引找到改行
select * from dbo.ChecksumCollision where checksum_col = hashbytes('MD5',N'1149077815')
結果
觀察執行計划,確定上面建的哈希索引是被使用到的
有1點需要說明的是:
不管是MD5或者SHA都是依賴數據類型來產生哈希值的,這就意味着N'Jerry'和'Jerry'產生的哈希值其實是一同的。像上面的查詢語句如果改成這樣則不會返回任何結果
關於性能,參考中的第三個鏈接有人對這些函數都做了性能比較。應該說SHA-512是最慢這點沒錯,畢竟用了整整64個字節來產生校檢值。MD5倒是稍稍比CHECKSUM慢上一點而已。
參考:
http://www.brentozar.com/archive/2013/05/indexing-wide-keys-in-sql-server/
https://technet.microsoft.com/zh-cn/library/ms174415(v=sql.105).aspx