65.9K
CodeProject is changing. Read more.
Home

String or Binary Data Would Be Truncated / Arithmetic Overflow Error Converting Numeric to Data Type Numeric – Workaround

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Mar 10, 2016

CPOL

2 min read

viewsIcon

8462

String or binary data would be truncated / arithmetic overflow error converting numeric to data type numeric – workaround

There’s nothing more annoying than getting the error ‘String or binary data would be truncated’ or ‘Arithmetic overflow error converting numeric to data type numeric’, when you need to insert data to a table using a SELECT statement. To make it more interesting, the SQL won’t be providing us the name of the column (or columns) which is causing this issue. (This is due to the SQL architecture on how it executes queries).

To illustrate this, I will use a small sample.

Suppose we have a table to store some Customer details:

CREATE TABLE Customer_Data(
    CustId        TINYINT
    ,CustFName    VARCHAR(10)
    ,CustLName    VARCHAR(10)
    ,MaxCredit    NUMERIC(6,2)
)

We will try to insert details to the above table. (In reality, the SELECT statement will be very complex and could fetch lots of rows).

INSERT INTO dbo.Customer_Data(
    CustId
    ,CustFName
    ,CustLName
    ,MaxCredit
)

SELECT 1 AS CustId,'John' AS CustFName,'Doe' AS CustLName,_
1000.00 AS MaxCredit UNION ALL
SELECT 2,'Jane','Doe',1000.00 UNION ALL
SELECT 3,'James','Whitacker Jr.',15000.00 

This will result in the following error:

Msg 8152, Level 16, State 14, Line 48
String or binary data would be truncated.
The statement has been terminated.

The challenge here is to find out actually which columns are having this issue. (As mentioned, in reality the number of columns could be very large).

However, there is a small workaround which we can use to find out the columns which is causing the insertion to fail. You need to do the following in order to find out these columns.

  1. First, create a table using the same select statement. (You can either create a temporary table or an actual table based on the environment and your need). I will create two tables, one actual and one temporary to illustrate both the options.
    SELECT A.*
    INTO Temp_Customer_Data
    FROM(
        SELECT 1 AS CustId,'John' AS CustFName,'Doe' AS CustLName,1000.00 AS MaxCredit UNION ALL
        SELECT 2,'Jane','Doe',1000.00 UNION ALL
        SELECT 3,'James','Whitacker Jr.',15000.00
    ) AS A
    
    SELECT A.*
    INTO #Customer_Data
    FROM(
        SELECT 1 AS CustId,'John' AS CustFName,'Doe' AS CustLName,1000.00 AS MaxCredit UNION ALL
        SELECT 2,'Jane','Doe',1000.00 UNION ALL
        SELECT 3,'James','Whitacker Jr.',15000.00
    ) AS A
  2. Use the following query to identify the issue columns:
    Actual Table
    ;WITH Cte_Source AS (
    SELECT
        C.COLUMN_NAME
        ,C.DATA_TYPE
        ,C.CHARACTER_MAXIMUM_LENGTH
        ,C.NUMERIC_PRECISION
        ,C.NUMERIC_SCALE
    FROM
        INFORMATION_SCHEMA.TABLES AS T
        JOIN INFORMATION_SCHEMA.COLUMNS AS C
            ON C.TABLE_CATALOG = T.TABLE_CATALOG
            AND C.TABLE_NAME = T.TABLE_NAME
            AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
    WHERE
        T.TABLE_NAME = 'Temp_Customer_Data'        -- Source Table
        AND T.TABLE_SCHEMA = 'dbo'
    )
    ,Cte_Destination AS (
    SELECT
        C.COLUMN_NAME
        ,C.DATA_TYPE
        ,C.CHARACTER_MAXIMUM_LENGTH
        ,C.NUMERIC_PRECISION
        ,C.NUMERIC_SCALE
    FROM
        INFORMATION_SCHEMA.TABLES AS T
        JOIN INFORMATION_SCHEMA.COLUMNS AS C
            ON C.TABLE_CATALOG = T.TABLE_CATALOG
            AND C.TABLE_NAME = T.TABLE_NAME
            AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
    WHERE
        T.TABLE_NAME = 'Customer_Data'        -- Destination Table
        AND T.TABLE_SCHEMA = 'dbo'
    )
    SELECT
        S.COLUMN_NAME
       ,S.DATA_TYPE
       ,S.CHARACTER_MAXIMUM_LENGTH
       ,S.NUMERIC_PRECISION
       ,S.NUMERIC_SCALE
    
       ,D.COLUMN_NAME
       ,D.DATA_TYPE
       ,D.CHARACTER_MAXIMUM_LENGTH
       ,D.NUMERIC_PRECISION
       ,D.NUMERIC_SCALE
    FROM
        Cte_Source AS S
        JOIN Cte_Destination AS D
            ON D.COLUMN_NAME = S.COLUMN_NAME
    WHERE
        S.CHARACTER_MAXIMUM_LENGTH > D.CHARACTER_MAXIMUM_LENGTH
        OR S.NUMERIC_PRECISION > D.NUMERIC_PRECISION
    Temporary Table
    ;WITH Cte_Source AS (
    SELECT
        C.COLUMN_NAME
        ,C.DATA_TYPE
        ,C.CHARACTER_MAXIMUM_LENGTH
        ,C.NUMERIC_PRECISION
        ,C.NUMERIC_SCALE
    FROM
        tempdb.sys.objects so
        JOIN tempdb.INFORMATION_SCHEMA.TABLES AS T
            ON so.name = T.TABLE_NAME
            AND so.[object_id] = OBJECT_ID('tempdb..#Customer_Data')
        JOIN tempdb.INFORMATION_SCHEMA.COLUMNS AS C
            ON C.TABLE_CATALOG = T.TABLE_CATALOG
            AND C.TABLE_NAME = T.TABLE_NAME
            AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
       
    WHERE
        T.TABLE_SCHEMA = 'dbo'
    )
    ,Cte_Destination AS (
    SELECT
        C.COLUMN_NAME
        ,C.DATA_TYPE
        ,C.CHARACTER_MAXIMUM_LENGTH
        ,C.NUMERIC_PRECISION
        ,C.NUMERIC_SCALE
    FROM
        INFORMATION_SCHEMA.TABLES AS T
        JOIN INFORMATION_SCHEMA.COLUMNS AS C
            ON C.TABLE_CATALOG = T.TABLE_CATALOG
            AND C.TABLE_NAME = T.TABLE_NAME
            AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
    WHERE
        T.TABLE_NAME = 'Customer_Data'        -- Destination Table
        AND T.TABLE_SCHEMA = 'dbo'
    )
    SELECT
        S.COLUMN_NAME
       ,S.DATA_TYPE
       ,S.CHARACTER_MAXIMUM_LENGTH
       ,S.NUMERIC_PRECISION
       ,S.NUMERIC_SCALE
    
       ,D.COLUMN_NAME
       ,D.DATA_TYPE
       ,D.CHARACTER_MAXIMUM_LENGTH
       ,D.NUMERIC_PRECISION
       ,D.NUMERIC_SCALE
    FROM
        Cte_Source AS S
        JOIN Cte_Destination AS D
            ON D.COLUMN_NAME = S.COLUMN_NAME
    WHERE
        S.CHARACTER_MAXIMUM_LENGTH > D.CHARACTER_MAXIMUM_LENGTH
        OR S.NUMERIC_PRECISION > D.NUMERIC_PRECISION

    Both the aforementioned queries will return the following result:

    image

The reason to return the above three columns is as follows:

  1. CustId ==> In our destination table, CustId’s data type is TINYINT. Even the select query is returning the results within the boundary, the data type which our insertion query is returning is an INT. So there could be a possibility that there could be large numbers that the destination table could not hold.
  2. CustName ==> ‘Whitacker Jr.’ is exceeding the maximum length of 10 which is in the destination table.
  3. MaxCredit ==> In the destination table, the size of the column is numeric (6,2) which means it can hold values up to 9999.99. But our insertion query contains a record which consists of 15000.00.

Hope this might be helpful to you.