【问题标题】:**Occasional** Arithmetic overflow error converting expression to data type int**偶尔**将表达式转换为数据类型 int 的算术溢出错误
【发布时间】:2015-11-09 14:56:16
【问题描述】:

我正在运行更新脚本来混淆数据,并且偶尔会遇到算术溢出错误消息,如标题所示。正在更新的表有 260k 条记录,但更新脚本需要运行多次才能产生错误。虽然它非常罕见,但在修复之前我不能依赖代码,因为调试起来很痛苦。

查看其他类似问题,这通常可以通过在表格或计算中将数据类型从 INT 更改为 BIGINT 来解决。但是,我看不出在哪里可能需要这样做。我已将脚本简化为以下内容,因为我已设法将其指向一列的更新。

更新正在调用一个函数,我已将其包含在下面。我怀疑,由于错误的随机性,使用 NEW_ID 函数可能会导致它,但是当我多次运行这部分函数时,我无法重新创建错误。 NEW_ID 函数不能在函数中使用,因此它是从视图中调用的,也包括在下面。

更新脚本:

UPDATE dbo.Addresses
SET HouseNumber = CASE WHEN LEN(HouseNumber) > 0 
                       THEN dbo.fn_GenerateRandomString (LEN(HouseNumber), 1, 1, 1) 
                       ELSE HouseNumber 
                  END

NEW_ID 视图和随机字符串函数

CREATE VIEW dbo.vw_GetNewID 
AS 
SELECT NEWID() AS New_ID

CREATE FUNCTION dbo.fn_GenerateRandomString (
@stringLength int, 
@upperCaseBit bit, 
@lowerCaseBit bit,
@numberBit bit
)
RETURNS nvarchar(100)
AS
BEGIN
-- Sanitise string length values.
IF ISNULL(@stringLength, -1) < 0
SET @stringLength = 0

-- Generate a random string from the specified character sets.
DECLARE @string nvarchar(100) = ''
SELECT
@string += c2
FROM
(
    SELECT TOP (@stringLength) c2 FROM (
        SELECT c1 FROM
        (
            VALUES ('A'),('B'),('C')
        ) AS T1(c1)
        WHERE @upperCaseBit = 1
        UNION ALL
        SELECT c1 FROM
        (
            VALUES ('a'),('b'),('c')
        ) AS T1(c1)
        WHERE @lowerCaseBit = 1
        SELECT c1 FROM
        (
            VALUES ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9')
        ) AS T1(c1)
        WHERE @numberBit = 1
        ) 
    AS T2(c2)
    ORDER BY (SELECT ABS(CHECKSUM(New_ID)) from vw_GetNewID)
) AS T2

RETURN @string
END

地址表(用于测试):

CREATE TABLE dbo.Addresses(HouseNumber nchar(32) NULL)

INSERT Addresses(HouseNumber)
VALUES ('DSjkmf jkghjsh35hjk h2jkhj3h jhf'),
    ('SDjfksj3548 ksjk'),
    (NULL),
    (''),
    ('2a'),
    ('1234567890'),
    ('An2b')

注意:地址表中只有 7k 行输入了值,即LEN(HouseNumber) &gt; 0

【问题讨论】:

    标签: sql sql-server sql-server-2012


    【解决方案1】:

    基于字符串的代码中的算术溢出令人困惑。但是有一件事可能会导致算术溢出。那是你的ORDER BY 子句:

    ORDER BY (SELECT ABS(CHECKSUM(New_ID)) from vw_GetNewID)
    

    CHECKSUM() 返回一个整数,其范围是 -2,147,483,648 到 2,147,483,647。请注意,最小数字的绝对值是 2,147,483,648,刚好超出范围。您可以验证SELECT ABS(CAST('-2147483648' as int)) 是否生成算术溢出错误。

    您不需要checksum()。唉,你确实需要这个视图,因为这个逻辑在一个函数中,NEWID() 是有副作用的。但是,您可以使用:

    ORDER BY (SELECT New_ID from vw_GetNewID)
    

    我怀疑您每百万行左右而不是每 40 亿行左右看到此信息的原因是因为作为排序过程的一部分,每行都对 ORDER BY 值进行了多次评估。最终,它会达到下限。

    编辑:

    如果您关心效率,使用字符串操作而不是表可能会更快。我可能会建议这个版本的函数:

    CREATE VIEW vw_rand AS SELECT rand() as rand;
    GO
    CREATE FUNCTION dbo.fn_GenerateRandomString (
        @stringLength int, 
        @upperCaseBit bit, 
        @lowerCaseBit bit,
        @numberBit bit
    )
    RETURNS nvarchar(100)
    AS
    BEGIN
        DECLARE @string NVARCHAR(255) = '';
    -- Sanitise string length values.
        IF ISNULL(@stringLength, -1) < 0
            SET @stringLength = 0;
        DECLARE @lets VARCHAR(255) = '';
        IF (@upperCaseBit = 1) SET @lets = @lets + 'ABC';
        IF (@lowerCaseBit = 1) SET @lets = @lets + 'abc';
        IF (@numberBit = 1) SET @lets = @lets + '0123456789';
    
        DECLARE @len int = len(@lets);
    
        WHILE @stringLength > 0 BEGIN
            SELECT @string += SUBSTRING(@lets, 1 + CAST(rand * @len as INT), 1)
            FROM vw_rand;
            SET @stringLength = @stringLength - 1;
        END;
        RETURN @string
    END;
    

    注意:rand() 被记录为不包括其范围的末尾,因此您不必担心它会准确返回 1。

    此外,此版本与您的版本略有不同,因为它可以多次提取同一个字母(因此也可以处理更长的字符串)。我认为这实际上是一个好处。

    【讨论】:

    • 非常感谢,这很有道理。正如您所建议的,我完全失去了 ABS 和 CHECKSUM 功能,并且它有效。我相信这也将有助于减少运行时间。不幸的是,需要保留对视图的引用,因为如果直接插入 NEWID(),我会收到消息 Invalid use of a side-effecting operator 'newid' within a function.. 再次感谢。
    • 非常有帮助的编辑谢谢。我用这个代替,它需要一半的时间!在运行新函数时,我注意到当CAST(rand * @len as INT) 返回 0 时,字符串长度偶尔会缩短。我将 WHILE 条件改为LEN(@String) &lt; @stringLength,并删除了SET @stringLength.. 语句,这运行良好。
    • @Fletch 。 . .编辑代码中的偏移量应该是1 + cast( . . . )
    猜你喜欢
    • 2011-10-07
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多