天天看点

修复identity 类型字段数据的跳跃

  修复identity 类型字段数据的跳跃(译)

---------------------------------------------------------------------------------------

转自http://www.chinaunix.net

----------------------------------------------------------------------------------------

内容提要

1、引言

2、dbcc object_atts

3、下载'sp_identity' 存储过程

4、使用sp_identity显示使用identity 类型字段的表

5  使用sp_identity显示当前表的 identity 数字

6、使用sp_identity 修改表的 identity数字

1引言

在sybase 中,有一个使用未公开的dbcc 命令 ,即 dbcc object_atts 快速修复identity 跳跃的方法,因为 dbcc

object_atts 比较难于使用,而存储过程 sp_identity 更容易完成你的问题

警告:本文的方法没有出现在sybase 发布的文档中,并且没有被 sybase 支持,使用与否取决定你自己,如果出现问题,不能从

sybase 技术支持那里得到帮助

2、dbcc object_atts

dbcc object_atts 命令提供了 一个访问 OAM 页的后门,在 OMA 中,保存了表的 identity 计数器。

不幸的是,这个命令在文档中几乎没有任何说明,并且很难使用,其语法如下:

  dbcc object_atts (table_name, 0, subcommand, [ new_value ] )

第一个参数 :表名,第二个参数 0,

第三个参数:   get  ---显示 保存在 OMA 中的 identity 值

           或put  ---设置 OMA 中的 identity 值,设置的具体数据为 第四个参数数据

第四个参数 :新设置的数

主要的问题是 dbcc object_atts 显示的数据为 16字节的 16进制字符,你必须自己解释为10进制,并且,如何解释依赖于

identity 列的长度,同样,设置它也是。在实际环境中,很少作为首选。

幸运的是,有一个存储过程 sp_identity 能够处理编码与解码, 并且给出所有含 identity 列的表的情况

下载 'sp_identity'

sp_identity 只支持 11。0 以后版本,下载文件中包含两个版本,一个用于 ASE 11.9/11.5/11.0 的叫  

,sp_identity.119.sql,另外一个用于ASE 12.0及以后版本(sp_identity.120.sql)

4、使用sp_identity显示使用identity 类型字段的表

sp_identity 有几个不同的功能,不带任何参数运行,只显示当前数据库中 包含 identity 列的表,

以及最大可能的 identity 跳跃

1>; sp_identity

2>; go

Tables with an identity column in database '':

Owner.Table.Column datatype     Maximum Identity Gap

------------------------------- -------------------------------

dbo.invoices.inv_nr numeric(18) 500000000000000 (burn)

dbo.small_gap_tab.a numeric(30) 10 (identity_gap)

dbo.my_table.n numeric(6)       500 (burn)

(burn) 表示 identity列的 一次取数由系统参数"identity burning set factor" 决定,而

(identity_gap) 表示 该表有自定义的identity_gap 属性

5  使用sp_identity显示当前表的 identity 数字

确定当前特定表的  OAM 页的identity 值,必须按照以下三步过程:

  首先 打开跟踪标志 3604 (运行 dbcc traceon(3604) )

  运行 指定特定表名运行 sp_identity

  1>; sp_identity invoices

  2>; go

  Table             = invoices (id=1804533462)

  Identity column   = a numeric(18)

  Max. Identity Gap = 500000000000000

         ("identity burning set factor" = 5000 = 0.05%)

  Reading identity value from OAM page...

  object_atts:get:return value=1

  0207E20C: 0001c6bf 52634001 00000000 00000000 ....Rc@.........

  0207E21C: .

  DBCC execution completed. If DBCC printed error messages,

  contact a user with System Administrator (SA) role.

  这个 16字节的 16进制字符串是 OAM 中保存的 identity 值 (0001c6bf 52634001 00000000 00000000)

  再次运行 sp_identity ,并且 指定这个字符串参数

  1>; sp_identity invoices, "0001c6bf 52634001 00000000 0000000"

  2>; go

  Table             = invoices (id=1804533462)

  Identity column   = a numeric(18)

  Max. Identity Gap = 500000000000000

         ("identity burning set factor" = 5000 = 0.05%)

  Decoded identity value on OAM page = 500000000000001

  (hex=0001c6bf52634001)

  现在,我们得到并解码了 OAM 中的 identity 值,但它意味着什么呢?

  首先,如果你用 "with nowait" 停止服务器,这个 identity 列将跳跃多少。而不是表中数据的下一个。

  而是保存在 OAM 中的数据加上 identity 种子设顶数(在 ASE 12.0, 为identity_gap 选项)

  另外,当你运行 ASE 12.0, 可以通过运行 dbcc listoam看到这个数据   

6、使用sp_identity 修改表的 identity数字

使用 sp_identity , OAM 中identity 的 数字可以被设置为任意数. 这个需要如下步骤:

1、确认你要设置的数据(查看表中identity 的数据应该到多少,比如 10033 )

2、正常停止服务器

3  重新启动服务器,以单用户模式,或者不让用户访问

4、 运行 sp_identity table_name, null, new_value ;

  1>; sp_identity invoices, null, 10032

  2>; go

  Table             = invoices (id=1804533462)

  Identity column   = a numeric(18)

  Max. Identity Gap = 500000000000000

       ("identity burning set factor" = 5000 = 0.05%)

  object_atts:put:return value=1

  DBCC execution completed. If DBCC printed error messages,

  contact a user with System Administrator (SA) role.

  Identity value on OAM page has been set to 10032

  (hex=0x00000000000027300000000000000000)

  You should now do a 'shutdown with nowait' immediately.

  After restarting the server, the value assigned to the

  next row inserted into 'invoices' will be 10033.

5  然后立即运行 'shutdown with nowait' ,这样 表的下一个  identity 数字是 10033

6  重新正常启动sybase 服务

[color=red][b]说明:如果你应用了 identity 数据类型,并且要求identity 列保持连续,并且数据量很大,那么,用这个方法需要的时间可能比较短

否则不推荐使用。[/b][/color]

下面是SP_IDENTITY.120.SQL,用于  12.0 以后版本

set nocount on

go

set flushmessage on

go

use sybsystemprocs

go

-- we need to be at ASE 12.0 or later; if not, abort this script

if isnull(object_id("master.dbo.sysqueryplans"),99) >;= 99

begin

   print ""

   print ""

   print "********************************************"

   print "********************************************"

   print " This script is for ASE 12.0 or later."

   print " Please install SP_IDENTITY.119.SQL instead."

   print "********************************************"

   print "********************************************"

   print " "

   print " "

   print ""

   set background on  -- terminate this script now

end

go

print ""

print "Installing 'sp_identity'..."

print ""

go

if object_id("sp_identity_help") <>; NULL

begin

   drop proc sp_identity_help

end

go

create proc sp_identity_help

as

begin

   print " "

   print " Usage: sp_identity table_name, ""hex-string-from-OAM-page"", new-identity-value "

   print " Notes: "

   print " - specifying only parameter 1 retrieves the current identity value for"

   print "   that table from the OAM page as a hexadecimal string."

   print " - specifying this hexadecimal string as the second parameter (in quotes)"

   print "   will decode the hex value to a numeric value."

   print " - specifying a numeric value for the third parameter will set that"

   print "   value as the new identity value for this table."

   print " - parameters 2 and 3 cannot be specified together: one of them must be NULL."

   print " - ""dbcc traceon(3604)"" must be run before using option 2 or 3"

   print " "

   print " Copyright (c) 2000-2002 Rob Verschoor/Sypron B.V."

   print " See http://www.sypron.nl/idfix.html for background information & updates."

   print " "

end

go

grant execute on sp_identity_help to public

dump tran sybsystemprocs with truncate_only

go

if object_id("sp_identity") <>; NULL

begin

   drop proc sp_identity

end

go

create proc sp_identity

@p0 varchar(50) = NULL, -- table name

@p1 varchar(50) = NULL, -- hex value to decode

@p2 numeric(38) = NULL  -- new value to set

as

begin

set nocount on

declare @n numeric(38), @j numeric(38), @n256 numeric(3), @n10 numeric(38)

declare @i int, @max int, @idlen int, @idlenb int, @ib int, @len int

declare @p0_id int, @b int, @b1 binary(1), @lsb int

declare @idgap int, @idburn int, @maxidgap numeric(38), @max1 int, @max2 int

declare @idburnpct numeric(5,2), @v int, @ntab int

declare @c2 char(2), @doampg int, @indid int, @dbname varchar(32)

declare @vc50 varchar(50), @colname varchar(32), @vb16 varbinary(16)

select @dbname = db_name()

-- check version

select @v = 0

if exists (select * from sysobjects

           where name = "sysqueryplans" and type = "S")

begin

    select @v = 12

end

if @p0 = '?'

begin

   exec sp_identity_help

   return 0

end

-- numeric values

select @n256 = 256

select @n10 = 10

-- id burning set factor

select @idburn = value from master.dbo.syscurconfigs where config=141

select @idburnpct = convert(numeric(5,2), @idburn * 0.00001)

-- figure out msb/lsb

select @lsb = 0

if substring(convert(binary(4), 1), 1 ,1) = 0x01 select @lsb = 1

if substring(convert(binary(4), 1), 2 ,1) = 0x01 select @lsb = 2

if substring(convert(binary(4), 1), 3 ,1) = 0x01 select @lsb = 3

if substring(convert(binary(4), 1), 4 ,1) = 0x01 select @lsb = 4

-- get all tables containing identity columns in this database

select id = so.id, owner = su.name, uid = so.uid, so.name, si.doampg, si.indid, idgap =

isnull(si.identitygap,0),

       colname = sc.name, sc.prec, maxgap = convert(numeric(38), ceiling((@idburn * 0.0000001) *

power(@n10,prec)))

into #id

from sysindexes si, syscolumns sc, sysobjects so, sysusers su

where si.indid < 2

  and so.type = "U"

  and sc.status & 128 = 128

  and so.id = sc.id

  and so.id = si.id

  and so.uid = su.uid

select @ntab = @@rowcount

set arithabort numeric_truncation off

if @p0 = null

begin

   -- display all tables with identity columns in the current DB

   if @ntab = 0

   begin

      print " There are no tables with an identity column in database '%1!'", @dbname

          return 0

   end

   print " Tables with an identity column in database '%1!':", @dbname

   print " "

   select @max1 = max(char_length(owner + "." + name + "." + colname)) + 12 from #id

   select @max2 = max(char_length(convert(varchar(38), maxgap))) + 7 from #id

   if @max1 <= 52 and @max2 <= 24

   begin

     select convert(varchar(52), owner + "." + name + "." + colname + " numeric(" +

convert(varchar(2),prec) + ")") "Owner.Table.Column datatype",

                    convert(varchar(24), case when idgap = 0 then convert(varchar(38),maxgap) + "

(burn)"

                       else convert(varchar, idgap) + " (identity_gap)"

                     end) "Maximum Identity Gap"

     from #id

     order by name

   end

   else

   if @max1 <= 32 and @max2 <= 44

   begin

     select convert(varchar(32), owner + "." + name + "." + colname + " numeric(" +

convert(varchar(2),prec) + ")") "Owner.Table.Column datatype",

                    convert(varchar(44), case when idgap = 0 then convert(varchar(38),maxgap) + "

(burn)"

                       else convert(varchar, idgap) + " (identity_gap)"

                     end) "Maximum Identity Gap"

     from #id

     order by name

   end

   else

   begin

     select convert(varchar(78), owner + "." + name + "." + colname + " numeric(" +

convert(varchar(2),prec) + ")") "Owner.Table.Column datatype",

                    convert(varchar(50), case when idgap = 0 then convert(varchar(38),maxgap) + "

(burn)"

                       else convert(varchar, idgap) + " (identity_gap)"

                  end) "Maximum Identity Gap"

      from #id

      order by name

   end

   print " "

   print " Legend:"

   print "    (burn)         : gap size is determined by ""identity burning set factor"" "

   if @v = 12

   begin

   print "    (identity_gap) : gap size is determined by the ""identity_gap"" setting"

   end

   print " "

   print " Current value for ""identity burning set factor"" = %1! (=%2!%%)", @idburn, @idburnpct

   return 0

end

if charindex("sa_role", show_role()) = 0

begin

   print "You must have 'sa_role' to run this procedure."

   return -1

end

if charindex("sybase_ts_role", show_role()) = 0

begin

   print "You must have 'sybase_ts_role' to run this procedure."

   return -1

end

-- get some info on the object

select @p0_id = object_id(@p0)

if @p0_id = NULL

begin

   print " Error: '%1!' is not a user table.", @p0

   return -1

end

if @p0 not like "%.%"

begin

  select @ntab = count(*) from sysobjects where name = @p0 and type = "U"

  if @ntab >; 1

  begin

     print " %1! tables named '%2!' exist in this database:", @ntab, @p0

         print " "

     select owner + "." + name "owner.table_name"

         from #id where name = @p0 order by uid

         print " "

     print " Specify 'owner.table_name' to identify the table."

         return -1

  end

end

select @doampg = doampg, @indid = indid, @idgap = idgap

from #id where id = @p0_id

if not exists (select 1 from syscolumns where id = @p0_id and status & 128 = 128)  

begin

   print " Error: Table '%1!' does not have an identity column", @p0

   return -1

end

-- retrieve the length of the numeric identity column as declared when the

-- table was created

select @len = prec, @colname = name

from syscolumns where id = @p0_id and status & 128 = 128

if @@rowcount = 0

begin

   print " Error: Could not retrieve length of identity column for table '%1!'", @p0

   return -1

end

select @idlen = @len -- column precision (positions)

select @idlenb = (@idlen+1-((@idlen+5)/12)-(@idlen/12))/2 -- column length (bytes)

print " "

print " Table             = %1! (id=%2!)", @p0, @p0_id

print " Identity column   = %1! numeric(%2!)", @colname, @len

--print " OAM page          = %1!", @doampg

--print " Bytes on OAM page = %1!", @idlenb

if @idgap >; 0

begin

   print " Max. Identity Gap = %1! (""identity_gap"" = %2!)", @idgap, @idgap

end

else

begin

   set arithabort numeric_truncation off

   select @maxidgap = ceiling((convert(numeric(38),@idburn) * 0.0000001) * power(@n10,@len))

   print " Max. Identity Gap = %1!", @maxidgap

   print "                     (""identity burning set factor"" = %1! = %2!%%)", @idburn, @idburnpct

   if @v = 12

   begin

   print "                     (Note: ""identity_gap"" has not been set for this table !)"   

   end

end

print " "

if @p1 = NULL and @p2 = NULL -- retrieve current ID value

begin

   print " "

   print " Reading identity value from OAM page... (traceflag 3604 should be enabled !)"

   print " "

   dbcc object_atts (@p0, 0, get)

   print " "

   print " To decode this hexadecimal value, re-run 'sp_identity' with the hexadecimal"

   print " string as a quoted 2nd parameter (you can leave the spaces in). "

   print " Example:  sp_identity %1!, ""the-hex-string-from-the-above-output"" ", @p0

   print " "

   --

   -- only for 12.0+, dbcc listoam will display the values in the OAM and the DES

   --

   --dbcc listoam(@dbname, @p0_id, @indid)

   --

   return 0

end

if upper(@p1) like "[ 0-9A-F]%" -- entered a hex string, decode it

begin

if @p2 != NULL

begin

   print " Error: When decoding a hex value from the OAM page, parameter 3"

   print " (the new identity value) must be NULL."

   exec sp_identity_help

   return -1

end

select @ib = @idlenb -- counts bytes

select @i  = char_length(@p1) -- counts string

select @n = 0 -- holds result

while 1 = 1

begin

   select @c2 = substring(@p1, (char_length(@p1)[email protected])+1, 2)

   --print "Step %1!, [%2!]", @ib, @c2

   if upper(substring(@c2,1,1)) = " " -- ignore single space pasted in by user

   begin

     select @i = @i - 1

         continue

   end

   if (upper(@c2) not like "[0-9A-F][0-9A-F]")

   begin

     print " Error: Invalid characters (%1!) in hex string", @c2

     break

   end

   -- add this byte to the result

   select @vc50 = @vc50 + @c2

   select @j = power(@n256, (@ib-1))

   select @n = @n + @j * hextoint("0x" + @c2)

   --print "Step %1!, exp= %2!, result = %3!", @ib, @j, @n

   -- next byte

   select @ib = @ib - 1

   if @ib = 0 break  -- ready

   -- next 2 chars

   select @i = @i - 2

   if @i = 0

   begin

      -- we shouldn't have got here, error

          print " Error: ID column length and specified hex string do not match !"

      break

   end

end

if @ib = 0

begin

   print " Decoded identity value on OAM page = %1!", @n

   print " (hex= %1!)", @vc50

   print " "

   return 0

end

else

begin

   print " Error: Aborted due to error."

   return -1

end

end

if @p2 != NULL -- entered a new ID value, set it

begin

if @p1 != NULL

begin

   print " Error: When setting a new identity value, parameter 2"

   print " (the hex string from the OAM page) must be NULL."

   exec sp_identity_help

   return -1

end

-- set the new ID value

if @p2 <= 0

begin

   print " Error: The new identity value must be >;= 0."

   print " "

   exec sp_identity_help

   return -1

end

if char_length(convert(varchar, @p2)) >; @idlen

begin

   select @vc50 = replicate("9", @idlen)

   print " Error: New identity value (%1!) is too large", @p2

   print "        for this identity column. The maximum possible value", @p2

   print "        is %1! (%2! positions).", @vc50, @idlen

   return -1

end

if @lsb = 0

begin

   print " "

   print "Internal error in 'sp_identity':"

   print "Cannot figure out byte order on this platform."

   print "Please send a note to '[email protected]' with the text of this"

   print "error and your @@version string."

   return -1

end

-- convert the numeric value to hex

select @ib = @idlenb -- counts bytes

select @i  = 15

select @vc50 = NULL -- holds result

select @vb16 = NULL -- holds result

select @n256 = 256

select @n = @p2

set arithabort numeric_truncation off

while 1 = 1

begin

   select @j = power(@n256, @i)

   select @b = @n / @j

   select @n = @n - (@b * @j)

   select @c2 = right(inttohex(@b),2)

   select @b1 = substring(convert(binary(4), @b), @lsb,1)

   select @vb16 = @vb16 + @b1

   --print "Step %1!, @b=%2!, hex(c2)=%3!, hex(b1)=%5!, result=%4!", @i, @b, @c2, @vb16, @b1

   select @i = @i - 1

   if @i < 0 break  -- ready

end

if @i < 0

begin

   select @vb16 = substring(@vb16, 16 - (@idlenb) + 1, (@idlenb) ) + substring(@vb16, 1, 16 -

(@idlenb) )

   -- set the new ID value

   dbcc object_atts(@p0, 0, "put", @vb16)

   print " "

   print " Identity value on OAM page has been set to %1!", @p2

   print " (hex= %1!)", @vb16

   print " "

   select @p2 = @p2 + 1

   print " You should now do a 'shutdown with nowait' immediately."

   print " After restarting the server, the value assigned to the next row "

   print " inserted into '%1!' will be %2!.", @p0, @p2

   print " "

   return 0

end

else

begin

   print " Error: Aborted due to error."

   return -1

end

end

-- we should never get here

print "Internal error in 'sp_identity': Reached invalid end of procedure."

return -1

end

go

grant execute on sp_identity to public

dump tran sybsystemprocs with truncate_only

go

print "Ready. For usage information, run 'sp_identity ""?"" '."

print ""

go