您的位置:首页 > 数据库 > > 正文

sql执行原理详解(Sql server中内部函数fn_PhysLocFormatter存在解析错误详解)

更多 时间:2021-10-26 11:22:26 类别:数据库 浏览量:2365

sql执行原理详解

Sql server中内部函数fn_PhysLocFormatter存在解析错误详解

前言

有网友指出,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之间的字节时,被认为是双字节字符而组合在一起参与反转操作,造成了错误。

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对开心学习网的支持。