前言
有網友指出,SQL Server 2012中fn_PhysLocFormatter內部函數在解析數據行記錄位置時存在錯誤,見:http://www.itpub.net/thread-1751655-1-1.html,實際測試后發現,一是2008R2中同樣存在問題,二是不僅頁號解析存在問題,槽號解析也存在同樣問題。
下面先查看表NT_SiteInfo的數據行記錄位置。
select SiteID,%%physloc%%,sys.fn_PhysLocFormatter(%%physloc%%) from NT_SiteInfo
SiteID | %%physloc%% | sys.fn_PhysLocFormatter(%%physloc%%) |
1 | 0xE900000001000000 | (1:59648:0) |
23 | 0xE900000001000100 | (1:59648:1) |
24 | 0xE900000001000200 | (1:59648:2) |
| ...... |
|
149 | 0xE900000001007F00 | (1:59648:127) |
150 | 0xE900000001008000 | (1:59648:128) |
151 | 0xE900000001008100 | (1:59648:33024) |
152 | 0xE900000001008200 | (1:59648:33280) |
| ...... |
|
226 | 0xE90000000100CC00 | (1:59648:52224) |
227 | 0xE90000000100CD00 | (1:59648:52480) |
228 | 0x4B02000001000000 | (1:587:0) |
229 | 0x4B02000001000100 | (1:587:1) |
| ...... |
|
360 | 0x4B02000001007F00 | (1:587:127) |
361 | 0x4B02000001008000 | (1:587:128) |
362 | 0x4B02000001008100 | (1:587:33024) |
363 | 0x4B02000001008200 | (1:587:33280) |
| ...... |
|
422 | 0x4B0200000100BD00 | (1:587:48384) |
423 | 0x4B0200000100BE00 | (1:587:48640) |
424 | 0x3C05000001000000 | (1:1340:0) |
425 | 0x3C05000001000100 | (1:1340:1) |
| ...... |
|
552 | 0x3C05000001008000 | (1:1340:128) |
553 | 0x3C05000001008100 | (1:1340:33024) |
|
|
|
596 | 0x3C0500000100AC00 | (1:1340:44032) |
597 | 0x9978000001000000 | (1:39288:0) |
| ...... |
|
658 | 0x9978000001003D00 | (1:39288:61) |
下面查看表NT_SiteInfo分配的數據頁情況。
dbcc ind(wjgk,nt_siteinfo,0)
PagePID | IAMFID | IAMPID | PageType | IndexLevel | NextPagePID | PrevPagePID |
238 | NULL | NULL | 10 | NULL | 0 | 0 |
233 | 1 | 238 | 1 | 0 | 587 | 0 |
587 | 1 | 238 | 1 | 0 | 1340 | 233 |
1340 | 1 | 238 | 1 | 0 | 30873 | 587 |
30873 | 1 | 238 | 1 | 0 | 0 | 1340 |
Microsoft未公開的偽列%%physloc%%,類型為Binary(8),返回表中記錄的RowID,格式是:前4字節表示頁號,中間2字節表示文件號,最后2字節表示槽號。
對照上面的實際數據,可以發現sys.fn_PhysLocFormatter
在解析記錄位置時,既有采用高字節在前的BIG_ENDIAN格式,又有采用低字節在前的LITTLE_ENDIAN格式,造成采用高字節在前的BIG_ENDIAN格式解析的數據錯誤:
頁號解析:
E9000000解析為59648(E900),錯誤,實際應為233(E9)
4B020000解析為576(24B),正確
3C050000解析為1340(53C),正確
99780000解析為39288(9978),錯誤,實際應為30873(7899)
槽號解析:
8000解析為128(0080),正確
8100解析為33024(8100),錯誤,應為129(0081)
下面給出錯誤原因。
先看下sys.fn_PhysLocFormatter
函數的定義:
select OBJECT_DEFINITION(object_id('sys.fn_PhysLocFormatter'))go --------------------------------------------------------------------------------- Name: sys.fn_PhysLocFormatter---- Description:-- Formats the output of %%physloc%% virtual column---- Notes:-------------------------------------------------------------------------------create function sys.fn_PhysLocFormatter (@physical_locator binary (8)) returns varchar (128)as begin declare @page_id binary (4) declare @file_id binary (2) declare @slot_id binary (2) -- Page ID is the first four bytes, then 2 bytes of page ID, then 2 bytes of slot -- select @page_id = convert (binary (4), reverse (substring (@physical_locator, 1, 4))) select @file_id = convert (binary (2), reverse (substring (@physical_locator, 5, 2))) select @slot_id = convert (binary (2), reverse (substring (@physical_locator, 7, 2))) return '(' + cast (cast (@file_id as int) as varchar) + ':' + cast (cast (@page_id as int) as varchar) + ':' + cast (cast (@slot_id as int) as varchar) + ')' end
再看下reverse函數:
select reverse('工人')----人工 (1 行受影響) select reverse('12345工人')---------人工54321 (1 行受影響) select reverse('工12345人')---------人54321工 (1 行受影響)
結論:問題出在reverse函數上。
reverse函數的作用是字符反轉,而不是字節反轉,當遇到81-FE之間的字節時,被認為是雙字節字符而組合在一起參與反轉操作,造成了錯誤。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。
|
新聞熱點
疑難解答