【问题标题】:How to figure out which column raises an arithmetic overflow error upon insert?如何确定插入时哪一列引发算术溢出错误?
【发布时间】:2010-09-08 11:20:28
【问题描述】:

想象一个包含一百个不同列的表格。那么,想象一下,我有一个用户数据表,我想将数据从该表复制到基表。所以我写了这个简单的插入选择语句,然后弹出这个错误。那么,找出哪一列引发错误的最优雅的方法是什么?

我对解决方案的最初想法是将其包装在一个事务中,我最终将回滚并使用一种分而治之的方法:

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

如果它失败了,那么失败的列就在另一半。然后我们继续这个过程,直到找到讨厌的列。

还有什么比这更优雅的吗?

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

【问题讨论】:

  • 如果您将 UserTable 的数据类型与 BaseTable 的数据类型匹配,那么您的插入应该没有任何问题。之后所需要做的就是找到有问题的 UserTable 列 .
  • @Lieven 嗯,UserTable 就是这样......一个没有任何限制的用户表,因为其中的数据来自 Excel 或 Access 之类的。
  • 我很同情。如果这是需要定期完成的事情,您不能创建一个脚本/存储过程来检查您的输入吗?宏和简单的选择会大有帮助,例如 'SELECT 'c1', CAST(c1) AS INTEGER FROM UserTable'。
  • 这也适用于简单的INSERT ... VALUES ... 语句——在这种情况下更难解决。 I submitted a Connect item for the column name to be added to the error messages – 这真的会让这变得容易得多。

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


【解决方案1】:

以下脚本将为Basetable 的每个整数列创建SELECT 语句。
执行生成的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'

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 假设我只有一行有很多列。 :)
    • 那你只需要看一行。
    • @Sam,你没抓住重点。有问题的不是行数,而是列数。
    【解决方案3】:

    我采用了 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')
    

    【讨论】:

      【解决方案4】:

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

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

      在其上运行查询,这样您就不必担心隐藏正在破坏它的列的事务。有时在错误中它会提示发生了什么。通常,如果您正在查看正在发生的事情,您可以看到文本何时进入 int,反之亦然。

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

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

      【讨论】:

      • 是的,它说:varbinary 中的算术溢出,值为 1239847234.000000。或者类似的东西。我绝对可以阅读错误消息,但这对我没有任何帮助。
      • 那么,您从 varchar 中提取的所有值以及您要插入到 varchar 中的所有值都是如此吗?因为 varbinary 往往让我认为这不是真的。
      • BaseTable 和 UserTable 中的列是否相同?
      • @Nathan_Stanford 我认为来自 UserTable 的值是 varchar 形式,它以 smallint 或 tinyint 或类似的形式插入到 BaseTable 中。无论如何,这不会改变任何事情。我想知道导致问题的列名,最好是快速。
      【解决方案5】:

      我认为你采取了错误的方法。如果您通过简单地从一个表中选择列并插入另一个表来获得算术溢出,那么您必须从更大的列(例如 bigint)中选择并插入到小列(例如 int)中。这是根本不正确的事情,您需要更改数据库结构,以便将行从一个表插入到另一个表中。检查每个表中的每一列,看看哪里可能出现溢出,然后调整您的目标表,以使您插入的数据适合。

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

      然后:

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

      【讨论】:

      • 不,我看不出我的方法有任何缺陷。我无法控制 UserTable,并且其中的所有列都是 varchar。我想要一种快速而肮脏的方法来确定哪一列导致错误。这只是数据的初始加载,不应该花费大量时间来检查这个或那个。你知道我的意思吗?
      • 这不是在回答我的问题,而是在回避它。
      • 所有列都是varchar?全部?
      • @Nathan_Stanford 没关系。连接到 Access 或其他任何东西的链接服务器!
      • 但是在BaseTable中它们不是varchar,假设它们在UserTable中都是varchar,以简化事情。这意味着您不需要将它们强制转换为 varchar。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-23
      • 1970-01-01
      相关资源
      最近更新 更多