如何轻松找到有溢出危险的 IDENTITY 列?

Posted

技术标签:

【中文标题】如何轻松找到有溢出危险的 IDENTITY 列?【英文标题】:How do I easily find IDENTITY columns in danger of overflowing? 【发布时间】:2012-01-05 16:12:10 【问题描述】:

我的数据库变旧了,我最大的 INT IDENTITY 列之一的价值约为 13 亿。这将溢出大约 21 亿。我计划增加它的大小,但由于数据库中的记录数量,我不想太快这样做。我可能会在增加列大小之前更换我的数据库硬件,这可以抵消这可能导致的任何性能问题。我还想关注我的数据库中所有其他列已满 50% 的列。表格很多,手动检查每一个是不切实际的。

这就是我现在获取值的方式(我知道返回的值可能有点过时,但对于我的目的来说已经足够了):

PRINT IDENT_CURRENT('MyDatabase.dbo.MyTable')

我可以使用INFORMATION_SCHEMA 获取此信息吗?

【问题讨论】:

这不是一个答案,但你的身份是从 0 还是 1 开始的?如果是,那么您是否考虑过将标识重置为最小的 int,而不是增加列大小?这将使您从相同的列大小中获得额外的 20 亿。 @AlexKuznetsov:是的,我已经考虑过了。我可能会在未来的项目中使用负值,但是为这个数据库编写的代码太多了,我不想冒险破坏它。 Here 和 here 就是原因。根据 SO FAQ,这种行为不仅是可以接受的,而且是被鼓励的。我将继续这样做,直到 SO 社区认为这是不礼貌的礼仪。 【参考方案1】:

您可以查阅sys.identity_columns系统目录视图:

SELECT     
    name,
    seed_value, increment_value, last_value
FROM sys.identity_columns

这将为您提供每列的名称、种子、增量和最后一个值。该视图还包含数据类型,因此您可以轻松找出哪些标识列可能很快就会用完数字...

【讨论】:

请注意,对于跨数据库视图,last_value 可能会错误地报告为 null。 IDENT_CURRENT(object_name(object_id)) 将返回正确的 last_value。【参考方案2】:

我创建了一个存储过程来解决这个问题。它使用INFORMATION_SCHEMA 查找IDENTITY 列,然后使用IDENT_CURRENT 和该列的DATA_TYPE 来计算已满百分比。将数据库指定为第一个参数,然后是可选的最小百分比和数据类型。

EXEC master.dbo.CheckIdentityColumns 'MyDatabase' --all

EXEC master.dbo.CheckIdentityColumns 'MyDatabase', 50 --columns 50% full or greater

EXEC master.dbo.CheckIdentityColumns 'MyDatabase', 50, 'int' --only int columns

示例输出:

Table                     Column             Type    Percent Full Remaining
------------------------- ------------------ ------- ------------ ---------------
MyDatabase.dbo.Table1     Table1ID           int     9            1,937,868,393
MyDatabase.dbo.Table2     Table2ID           int     5            2,019,944,894
MyDatabase.dbo.Table3     Table3ID           int     9            1,943,793,775

我创建了一个提醒,每月检查一次我的所有数据库,并将此信息记录在电子表格中。

CheckIdentityColumns 过程

USE master
GO

CREATE PROCEDURE dbo.CheckIdentityColumns
    (
    @Database       AS NVARCHAR(128),
    @PercentFull    AS TINYINT          = 0,
    @Type           AS VARCHAR(8)       = NULL
    )

AS

--this procedure assumes you are not using negative numbers in your identity columns

DECLARE @Sql NVARCHAR(3000)

SET @Sql = 
'USE ' + @Database + '

SELECT                        
    [Column].TABLE_CATALOG + ''.'' +
    [Column].TABLE_SCHEMA + ''.'' +
    [Table].TABLE_NAME          AS [Table],
    [Column].COLUMN_NAME                        AS [Column],
    [Column].DATA_TYPE              AS [Type],
    CAST((
    CASE LOWER([Column].DATA_TYPE)
    WHEN ''tinyint''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 255)  
    WHEN ''smallint''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 32767)  
    WHEN ''int''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 2147483647)  
    WHEN ''bigint''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 9223372036854775807)  
    WHEN ''decimal''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / (([Column].NUMERIC_PRECISION * 10) - 1))  
    END * 100) AS INT)                  AS [Percent Full],
    REPLACE(CONVERT(VARCHAR(19), CAST(
    CASE LOWER([Column].DATA_TYPE)
    WHEN ''tinyint''
    THEN (255 - IDENT_CURRENT([Table].TABLE_NAME))  
    WHEN ''smallint''
    THEN (32767 - IDENT_CURRENT([Table].TABLE_NAME))  
    WHEN ''int''
    THEN (2147483647 - IDENT_CURRENT([Table].TABLE_NAME))  
    WHEN ''bigint''
    THEN (9223372036854775807 - IDENT_CURRENT([Table].TABLE_NAME))  
    WHEN ''decimal''
    THEN ((([Column].NUMERIC_PRECISION * 10) - 1) - IDENT_CURRENT([Table].TABLE_NAME))  
    END
    AS MONEY) , 1), ''.00'', '''')              AS Remaining


FROM                       
    INFORMATION_SCHEMA.COLUMNS                  AS [Column]

    INNER JOIN    
    INFORMATION_SCHEMA.TABLES                   AS [Table]
    ON      [Table].TABLE_NAME                  = [Column].TABLE_NAME

WHERE
    COLUMNPROPERTY(
        OBJECT_ID([Column].TABLE_NAME), 
        [Column].COLUMN_NAME, ''IsIdentity'') = 1 --true
    AND [Table].TABLE_TYPE                      = ''Base Table'' 
    AND [Table].TABLE_NAME                      NOT LIKE ''dt%'' 
    AND [Table].TABLE_NAME                      NOT LIKE ''MS%'' 
    AND [Table].TABLE_NAME                      NOT LIKE ''syncobj_%''
    AND CAST(
    (
    CASE LOWER([Column].DATA_TYPE)
    WHEN ''tinyint''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 255)  
    WHEN ''smallint''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 32767)  
    WHEN ''int''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 2147483647)  
    WHEN ''bigint''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / 9223372036854775807)  
    WHEN ''decimal''
    THEN (IDENT_CURRENT([Table].TABLE_NAME) / (([Column].NUMERIC_PRECISION * 10) - 1))  
    END * 100
    ) AS INT)                                    >= ' + CAST(@PercentFull AS VARCHAR(4))

IF (@Type IS NOT NULL) 
    SET @Sql = @Sql + 'AND  LOWER([Column].DATA_TYPE)    = ''' + LOWER(@Type) + ''''

SET @Sql = @Sql + '

ORDER BY
    [Column].TABLE_CATALOG + ''.'' +
    [Column].TABLE_SCHEMA + ''.'' +
    [Table].TABLE_NAME,
    [Column].COLUMN_NAME'

EXECUTE sp_executesql @Sql
GO

【讨论】:

【参考方案3】:

Keith Walton 有一个非常全面的查询,非常好。下面是一个更简单的方法,它基于标识列都是整数的假设:

SELECT sys.tables.name AS [Table Name], 
    last_value AS [Last Value],     
    MAX_LENGTH,
    CAST(cast(last_value as int) / 2147483647.0 * 100.0 AS DECIMAL(5,2)) 
        AS [Percentage of ID's Used], 
    2147483647 - cast(last_value as int) AS Remaining
FROM sys.identity_columns
    INNER JOIN sys.tables
        ON sys.identity_columns.object_id = sys.tables.object_id
ORDER BY last_value DESC

结果将如下所示:

Table Name      Last Value      MAX_LENGTH  Percentage of ID's Used   Remaining
My_Table        49181800             4               2.29             2098301847

Checking Integer Identity Columns

【讨论】:

【参考方案4】:

在为这个问题设计 a solution 时,我们发现这个线程既丰富又有趣(我们还写了一个详细的 post about this 并描述了我们的工具是如何工作的)。

在我们的解决方案中,我们查询 information_schema 以获取 所有列。然后我们编写了一个程序,将遍历它们中的每一个并计算最大值和最小值(我们同时考虑上溢和下溢)。

SELECT
    b.COLUMN_NAME,
    b.COLUMN_TYPE,
    b.DATA_TYPE,
    b.signed,
    a.TABLE_NAME,
    a.TABLE_SCHEMA
FROM (
    -- get all tables
    SELECT
    TABLE_NAME, TABLE_SCHEMA
    FROM information_schema.tables
    WHERE 
    TABLE_TYPE IN ('BASE TABLE', 'VIEW') AND
    TABLE_SCHEMA NOT IN ('mysql', 'performance_schema')
) a
JOIN (
    -- get information about columns types
    SELECT
    TABLE_NAME,
    COLUMN_NAME,
    COLUMN_TYPE,
    TABLE_SCHEMA,
    DATA_TYPE,
    (!(LOWER(COLUMN_TYPE) REGEXP '.*unsigned.*')) AS signed
    FROM information_schema.columns
) b ON a.TABLE_NAME = b.TABLE_NAME AND a.TABLE_SCHEMA = b.TABLE_SCHEMA
ORDER BY a.TABLE_SCHEMA DESC;

【讨论】:

以上是关于如何轻松找到有溢出危险的 IDENTITY 列?的主要内容,如果未能解决你的问题,请参考以下文章

如何添加水平滚动条以访问 Material-UI 表中的溢出列

IDENTITY 列重置编号

如何去除SqlServer的自增字段 不用identity_insert和创建新列 还有别的方法么? 这个列里有数据,不能动

如何在 Redshift 中打开/关闭 IDENTITY_INSERT

如何将 oracle 中的 IDENTITY 列重置为新值

列属性:RowGUIDCol 和 Identity