【问题标题】:SQL Server Conversion Failure when using INSERT使用 INSERT 时 SQL Server 转换失败
【发布时间】:2017-12-09 02:13:15
【问题描述】:

我有一个现有的充满数据的表,可以使用

创建
CREATE TABLE __EpisodeCost
(
    ActivityRecordID INT NOT NULL, 
    ActCstID NVARCHAR(15), 
    VolAmt FLOAT, 
    ActCnt FLOAT, 
    TotCst FLOAT, 
    ResCstID NVARCHAR(50)
);

这来自我无法控制的提要,我想将其转换为我自己的版本,名为 EpisodeCost

CREATE TABLE EpisodeCostCtp
(
    ActivityRecordID INT NOT NULL, 
    ActCstID NVARCHAR(6), 
    ResCstID NVARCHAR(7), 
    ActCnt NVARCHAR(7), 
    TotCst DECIMAL(18, 8) 
);

现在,我遇到的问题是转换。我可以执行查询

SELECT 
    ActivityRecordID, 
    Cast(ActCstID AS NVARCHAR(6)), 
    Cast(ResCstID AS NVARCHAR(7)), 
    Cast(LTRIM(STR(ActCnt, 10)) AS NVARCHAR(7)), 
    Cast(TotCst AS DECIMAL(18, 8)) 
FROM __EpisodeCostCtp;

但是,当我尝试执行时,它会提供数据

INSERT INTO EpisodeCostCtp 
    (
        ActivityRecordID, 
        ActCstID, 
        ResCstID, 
        ActCnt, 
        TotCst 
    ) 
SELECT 
    ActivityRecordID, 
    Cast(ActCstID AS NVARCHAR(6)), 
    Cast(ResCstID AS NVARCHAR(7)), 
    Cast(LTRIM(STR(ActCnt, 10)) AS NVARCHAR(7)), 
    Cast(TotCst AS DECIMAL(18, 8)) 
FROM __EpisodeCostCtp;

我明白了

消息 8115,第 16 级,状态 8,第 102 行 将数字转换为数字数据类型的算术溢出错误。 声明已终止。

为什么我可以SELECT 使用相关的转换,但不能INSERT 进入目标表?


编辑。我仍然不完全知道这里发生了什么。

根据 Serg 的建议,我试图定位有问题的记录,但查询

SELECT 
    ActivityRecordID, 
    Cast(ActCstID AS NVARCHAR(6)), 
    Cast(ResCstID AS NVARCHAR(7)), 
    Cast(LTRIM(STR(ActCnt, 10)) AS NVARCHAR(7)), 
    Cast(TotCst AS DECIMAL(18, 8))      
FROM __EpisodeCostCtp
WHERE TotCst > 9.999999999999999e9;

返回零记录。更改为 9.999999999999999e8 确实如此,并且转换/转换发生时没有错误。我已经将INSERT 查询更改为使用DECIMAL(36, 18),现在插入成功,但我仍然不明智。显然,我的演员阵容受到了限制,但为什么SELECT 有效而INSERT 失败,我仍然不知道。

【问题讨论】:

  • 将表作为__EpisodeCost 引入,然后显示两个查询__EpisodeCostCtp 的查询有什么意义吗?
  • 目标表上没有任何触发器?
  • @Damien_The_Unbeliever 不,此时任何一张桌子上都没有 PK/FK。这很简单,从一个表格中选择数据到另一个格式略有不同的表格中。两个表上都没有触发器。
  • 您可以尝试在INSERT 之前使用SET NUMERIC_ROUNDABORT OFFSET ... OFF; INSERT ... SELECT ... FROM...; 吗?
  • 您能否展示一些产生此问题的示例数据? (理想情况下作为插入语句)?我用虚拟数据试了一下,效果很好。

标签: sql sql-server sql-server-2008 type-conversion


【解决方案1】:

这是一个溢出问题,您可能想尝试

SET ARITHABORT OFF

来自https://docs.microsoft.com/en-us/sql/t-sql/statements/insert-transact-sql

当 INSERT 语句遇到算术错误(溢出、 除以零或域错误)在表达式期间发生 评估,数据库引擎处理这些错误就像 SET ARITHABORT 设置为 ON。批处理已停止,并且出现错误消息 回来。在表达式评估期间,当 SET ARITHABORT 和 SET 如果 INSERT、DELETE 或 UPDATE 语句,ANSI_WARNINGS 为 OFF 遇到算术错误、溢出、被零除或域 错误,SQL Server 插入或更新 NULL 值。如果目标 列不可为空,插入或更新操作失败,用户 收到错误。

【讨论】:

  • 很好,我不知道这个。
  • 这让我能够解决这个问题。 @Serg 上面所说的似乎是正确的。 SELECT 并未作用于所有记录。
  • 这可能是 ArithAbort 和 ansi_warnings 默认关闭的情况。您可以在 SSMS 上进行检查,或者在 Select 语句之前将两者都设置为 on 并尝试。
【解决方案2】:

要查找导致问题的行,请尝试

SELECT *       
FROM __EpisodeCostCtp
WHERE TotCst > 9.999999999999999e9;

【讨论】:

    【解决方案3】:

    我相信您的 TotCst 列中的值大于 DECIMAL(18,8)。 如果目标类型不够大,SQL SERVER 版本 2008 会得到四舍五入的结果。但它不能插入,因为该表只接受精确值。

    您可以在 SQL SERVER 2012 上看到我的两个查询的结果:

     DECLARE @float FLOAT 
     SET @float = 12345678911.12
     SELECT CAST(@float AS decimal(18,8))
    

    结果: 将浮点数转换为数值数据类型时发生算术溢出错误。

    使用 DECIMAL(19,8) 代替相同的查询:

     DECLARE @float FLOAT 
     SET @float = 12345678911.12
     SELECT CAST(@float AS decimal(19,8))
    

    结果:1​​2345678911.12000100

    编辑:检查您的查询工具是否获取所有记录。如果不是,可能在其余记录中,有一些值大于 DECIMAL(18.8)

    【讨论】:

    • 如果是这种情况,当他自己运行“选择”语句时,OP 会得到错误吗?
    • 通常是的。但是有一些奇怪的东西。我会在 SQL SERVER 2008 上试试这个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-26
    • 2014-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多