如何确定哪一列在插入时引发算术溢出错误?

时间:2010-09-08 11:20:28

标签: sql-server sql-server-2005 tsql sql-server-2008

想象一下有一百个不同列的表格。想象一下,我有一个用户数据表,我希望将数据复制到基表。所以我写了这个简单的insert-select语句,弹出这个错误。那么,找出哪一列引起错误的最优雅方法是什么呢?

我对解决方案的初步想法是将它包装在一个事务中,我将最终回滚并使用一种Divide and Conquer方法:

begin tran

insert into BaseTable (c1,c2,c3,...,cN)
select c1,c2,c3,...,cN 
from UserTable

rollback tran

这显然失败了。所以我们将列集分成两半:

begin tran

insert into BaseTable (c1,c2,c3,...,cK) --where K = N/2
select c1,c2,c3,...,cK --where K = N/2
from UserTable

rollback tran

如果失败则失败的列在另一半。我们继续这个过程,直到找到讨厌的专栏。

还有什么比这更优雅的?

注意:我也发现了这个问题的近乎重复,但它几乎没有答案。

5 个答案:

答案 0 :(得分:6)

以下脚本会为SELECT的每个整数列创建Basetable个语句 执行生成的SELECT语句应查明Usertable中的违规列。

SELECT  'PRINT ''' 
        + sc.Name 
        + '''; SELECT MIN(CAST(' 
        + sc.Name 
        + ' AS INTEGER)) FROM Usertable'
FROM    sys.columns sc 
        INNER JOIN sys.types st ON st.system_type_id = sc.system_type_id
WHERE   OBJECT_NAME(Object_ID) = 'BaseTable'
        AND st.name = 'INT'

答案 1 :(得分:0)

如果这只是您手动运行的内容,那么根据您插入的数据量,您可以使用OUTPUT子句将插入的行输出到客户端。

输出的最后一行之后的行应该是有问题的那一行。

答案 2 :(得分:0)

我采用了Lieven Keersmaekers的方法但扩展了它。如果表具有各种数字字段长度,则此脚本将根据类型名称和精度更改Cast。考虑到这个解决方案,信用仍然留给了Lieven - 它给了我很多帮助。

DECLARE @tableName VARCHAR(100)

SET @tableName = 'tableName'

SELECT 'PRINT ''' + sc.NAME + '''; SELECT MIN(CAST([' + sc.NAME + '] as ' + CASE 
        WHEN st.NAME = 'int'
            THEN 'int'
        ELSE st.NAME + '(' + cast(sc.precision AS VARCHAR(5)) + ',' + cast(sc.scale AS VARCHAR(5)) + ')'
        END + ')) from ' + @tableName
FROM sys.columns sc
INNER JOIN sys.types st ON st.system_type_id = sc.system_type_id
WHERE OBJECT_NAME(Object_ID) = @tableName
    AND st.NAME NOT IN ('nvarchar', 'varchar', 'image', 'datetime', 'smalldatetime', 'char', 'nchar')

答案 3 :(得分:-1)

很多时候你建议的蛮力方法是最好的方法。

但是,如果您有数据库副本,也可以发布虚假数据。

对它运行查询,这样你就没有关于隐藏破坏它的列的transcation。有时候在错误中它会给出一个提示,说明是什么。通常,如果您正在查看正在发生的事情,您可以看到文本何时进入int,反之亦然。

我这样做,这会导致我的代码中的任何其他内容导致问题。

您需要获取生成的查询的副本,您可以将其复制并粘贴到查询工具中。

答案 4 :(得分:-1)

我认为你采取了错误的做法。如果通过简单地从一个表中选择列并插入另一个表来获得算术溢出,则必须从较大的colimns(例如bigint)中选择并插入小列(例如int)。这基本上是不正确的事情,你需要改变你的数据库结构,以便从一个表插入另一个表中的行将起作用。检查每个表中的每一列,看看可能出现溢出的位置,然后调整目标表,以便插入的数据适合。

如果你想要一个快速而肮脏的解决方案,我仍然认为我的观点仍然存在,但回应你的意见。在BaseTable varchar(MAX)中创建所有列。

然后:

insert into BaseTable (c1,c2,...,cN)
select CAST(c1 AS varchar(max)),CAST(c2 AS varchar(max))...,cN 
from UserTable