SQL Server Conversion Failure when using INSERT - sql

I have an existing table full of data, which can be created using
CREATE TABLE __EpisodeCost
(
ActivityRecordID INT NOT NULL,
ActCstID NVARCHAR(15),
VolAmt FLOAT,
ActCnt FLOAT,
TotCst FLOAT,
ResCstID NVARCHAR(50)
);
This comes from a feed I have no control over and I want to convert this to my own version called EpisodeCost
CREATE TABLE EpisodeCostCtp
(
ActivityRecordID INT NOT NULL,
ActCstID NVARCHAR(6),
ResCstID NVARCHAR(7),
ActCnt NVARCHAR(7),
TotCst DECIMAL(18, 8)
);
Now, the problem I am having is with conversions. I can execute the query
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;
and it provides data, however, when I try to execute
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;
I get
Msg 8115, Level 16, State 8, Line 102
Arithmetic overflow error converting numeric to data type numeric.
The statement has been terminated.
Why can I SELECT using the relevant casts, but then cannot INSERT into the target table?
Edit. I still don;t fully know what is occurring here.
As per Serg's recommendations, I have attempted to locate the problematic records but the query
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;
returns zero records. Changing to 9.999999999999999e8 does, and the conversion/cast happens with out error. I have scince changed the INSERT query to use DECIMAL(36, 18) and now the insert succeeds, but I am still none the wiser. Clearly I was hitting a limit on the cast, but why SELECT works and INSERT fails, I still don't know.

as is an overflow issue you might want to try
SET ARITHABORT OFF
from https://learn.microsoft.com/en-us/sql/t-sql/statements/insert-transact-sql
When an INSERT statement encounters an arithmetic error (overflow,
divide by zero, or a domain error) occurring during expression
evaluation, the Database Engine handles these errors as if SET
ARITHABORT is set to ON. The batch is stopped, and an error message is
returned. During expression evaluation when SET ARITHABORT and SET
ANSI_WARNINGS are OFF, if an INSERT, DELETE or UPDATE statement
encounters an arithmetic error, overflow, divide-by-zero, or a domain
error, SQL Server inserts or updates a NULL value. If the target
column is not nullable, the insert or update action fails and the user
receives an error.

I believe that in your TotCst column has values bigger than DECIMAL(18,8).
And the version 2008 of SQL SERVER gets a rounded result if the destination type is not large enough. But it can't insert because the table accepts exact values only.
You can see the result of my two queries on SQL SERVER 2012 :
DECLARE #float FLOAT
SET #float = 12345678911.12
SELECT CAST(#float AS decimal(18,8))
Result :
An arithmetic overflow error occurred when converting float to numeric data type.
the same query with DECIMAL(19,8) instead :
DECLARE #float FLOAT
SET #float = 12345678911.12
SELECT CAST(#float AS decimal(19,8))
Result : 12345678911.12000100
EDIT : Check if your query tool fetchs all records. If not, maybe among the rest of records there are some values bigger than DECIMAL(18.8)

To find rows which cause the problem try
SELECT *
FROM __EpisodeCostCtp
WHERE TotCst > 9.999999999999999e9;

Related

Error in store procedure execution

When I am trying t execute a stored procedure, It is showing this error
Msg 8152, Level 16, State 14, Procedure MA01003_SUM1, Line 12
String or binary data would be truncated.
The statement has been terminated.
Following is the stored procedure
ALTER procedure [dbo].[MA01003_SUM1]
as
Begin
declare #desc as varchar(50)
set #desc = dbo.sdescription(1,0,3)
declare #cost as float
set #cost = dbo.SCost(0)
Insert into SummaryLoad(
SL_TierName,
SL_CorporateName,
SL_HospiceName,
SL_GroupName,
SL_DateKey,
SL_FactAmt,
SL_AHT,
SL_headingNo,
SL_Staffno,
SL_factno,
SL_Description,
SL_Cost)
SELECT [MA_TierName]
,[MA_CorporateName]
,[MA_HospiceName]
,[MA_GroupName]
,[MA_Datekey]
,SUM([MA_NumContacts]) Contacts
,SUM([MA_Duration]) ActualHandleTime,
'1',
'0',
'3',
#desc,
#cost
FROM [DM_ResourceUtilization].[dbo].[MedicationsAdded]
GROUP BY [MA_TierName]
,[MA_CorporateName]
,[MA_HospiceName]
,[MA_GroupName]
,[MA_Datekey]
end
Probable are the chances that data type or width of table SummaryLoad is not matching with that of select statement output. Please see the following script to illustrate the concept -
use tempdb
go
create table #test ( c1 varchar(5))
go
declare #desc as varchar(10) -- Width is 10 only
set #desc = 'AAAAAABBBBBBBBBBBbbCCCCCCCCCCCCCCcDDDDDDDDDDDDDD' -- Assigning higher width value
select #desc -- No error thrown, SQL server do the data truncation silently
insert into #test(c1) values ( #desc ) -- Here SQL server throws an error,
-- reason - value in #desc is of width 10, but target column width is 5
Better you can find the offending column or statement by yourself without much difficulty. To do this comment out each column and its values from the procedure and then invoke it in a transaction OR give a constant value that would match with the target data type for each column one by one. Sample follows -
Insert into SummaryLoad(
SL_TierName,
SL_CorporateName,
SL_HospiceName,
SL_GroupName,
SL_DateKey,
SL_FactAmt,
SL_AHT,
SL_headingNo,
SL_Staffno,
SL_factno,
SL_Description,
SL_Cost)
SELECT
/*[MA_TierName] */ -- Commented out
'A' -- Sample value, alter the procedure with this insert and run in transaction
-- If that is successful, this is offending column. Change the value with matching
-- data type and width of column SL_TierName of table SummaryLoad
-- Repeat this for all columns till you find the offending column(s)
,[MA_CorporateName]
,[MA_HospiceName]
,[MA_GroupName]
,[MA_Datekey]
,SUM([MA_NumContacts]) Contacts
,SUM([MA_Duration]) ActualHandleTime,
'1',
'0',
'3',
#desc,
#cost
FROM [DM_ResourceUtilization].[dbo].[MedicationsAdded]
GROUP BY [MA_TierName]
,[MA_CorporateName]
,[MA_HospiceName]
,[MA_GroupName]
,[MA_Datekey]
Modified procedure should be called in a transaction to ensure that your data is not affected by this test. Procedure can be called in a transaction using following statement
begin transaction
exec [dbo].[MA01003_SUM1]
rollback transaction
As per your error message :
Msg 8152, Level 16, State 14, Procedure MA01003_SUM1, Line 12
String or binary data would be truncated.
The statement has been terminated.
It is possibly your column width issue which is overlapped or oveflow then the size defined in column.
So, you have to just check which column have how much size & what you passing the data (i.e. size of data) which greater then size defined in column.

SQL Type Conversion Failure Still Exists Outside of Set

Good afternoon folks.
I'll preface this with "this was a hard question to ask". I'm running into conversion errors when I think I've got that part covered. Obviously, I do not.
The situation: VARCHAR field with INT data in it, plus some random garbage strings that are causing conversion issues. Here's an example of what I'm trying to do.
DECLARE #MyTABLE TABLE (
Value VARCHAR(50) NOT NULL
);
-- insert some strings
INSERT INTO #MyTABLE (Value) VALUES('400'), ('H-100'), ('H-200'), ('500'),
('600'), ('H-300');
-- conversion fails for the actual strings
SELECT *
FROM #MyTABLE m
WHERE CAST(m.Value AS INT) BETWEEN 1 AND 1000;
-- what I THOUGHT would fix it, but doesn't...
SELECT *
FROM (SELECT * FROM #MyTABLE WHERE Value NOT LIKE 'H%') X
WHERE CAST(X.Value AS INT) BETWEEN 1 AND 1000;
I realize there are other ways that I can do this, such as inserting all BUT the bad data into a temp table and querying that, but I'd like to know why my query doesn't work and what I could do to fix it.
EDIT - This is for SQL 2008 R2
Thanks in advance!
It is an odd bit, if you notice it's the BETWEEN clause that kills it, because the WHERE isn't being evaluated when you think it should, you can use:
SELECT *
FROM #MyTABLE
WHERE CASE WHEN ISNUMERIC(Value)=1 THEN value ELSE 0 END
BETWEEN 1 AND 1000
Demo: SQL Fiddle

String or binary data would be truncated. The statement has been terminated

I have met some problem with the SQL server, this is the function I created:
ALTER FUNCTION [dbo].[testing1](#price int)
RETURNS #trackingItems1 TABLE (
item nvarchar NULL,
warehouse nvarchar NULL,
price int NULL
)
AS
BEGIN
INSERT INTO #trackingItems1(item, warehouse, price)
SELECT ta.item, ta.warehouse, ta.price
FROM stock ta
WHERE ta.price >= #price;
RETURN;
END;
When I write a query to use that function like the following it getting the error
String or binary data would be truncated. The statement has been terminated
How can I fix this problem?
select * from testing1(2)
This is the way I create the table
CREATE TABLE stock(item nvarchar(50) NULL,
warehouse nvarchar(50) NULL,
price int NULL);
When you define varchar etc without a length, the default is 1.
When n is not specified in a data definition or variable declaration statement, the default length is 1. When n is not specified with the CAST function, the default length is 30.
So, if you expect 400 bytes in the #trackingItems1 column from stock, use nvarchar(400).
Otherwise, you are trying to fit >1 character into nvarchar(1) = fail
As a comment, this is bad use of table value function too because it is "multi statement". It can be written like this and it will run better
ALTER FUNCTION [dbo].[testing1](#price int)
RETURNS
AS
SELECT ta.item, ta.warehouse, ta.price
FROM stock ta
WHERE ta.price >= #price;
Of course, you could just use a normal SELECT statement..
The maximal length of the target column is shorter than the value you try to insert.
Rightclick the table in SQL manager and go to 'Design' to visualize your table structure and column definitions.
Edit:
Try to set a length on your nvarchar inserts thats the same or shorter than whats defined in your table.
In my case, I was getting this error because my table had
varchar(50)
but I was injecting 67 character long string, which resulted in thi error. Changing it to
varchar(255)
fixed the problem.
Specify a size for the item and warehouse like in the [dbo].[testing1] FUNCTION
#trackingItems1 TABLE (
item nvarchar(25) NULL, -- 25 OR equal size of your item column
warehouse nvarchar(25) NULL, -- same as above
price int NULL
)
Since in MSSQL only saying only nvarchar is equal to nvarchar(1) hence the values of the column from the stock table are truncated
SQL Server 2016 SP2 CU6 and SQL Server 2017 CU12
introduced trace flag 460 in order to return the details of truncation warnings.
You can enable it at the query level or at the server level.
Query level
INSERT INTO dbo.TEST (ColumnTest)
VALUES (‘Test truncation warnings’)
OPTION (QUERYTRACEON 460);
GO
Server Level
DBCC TRACEON(460, -1);
GO
From SQL Server 2019 you can enable it at database level:
ALTER DATABASE SCOPED CONFIGURATION
SET VERBOSE_TRUNCATION_WARNINGS = ON;
The old output message is:
Msg 8152, Level 16, State 30, Line 13
String or binary data would be truncated.
The statement has been terminated.
The new output message is:
Msg 2628, Level 16, State 1, Line 30
String or binary data would be truncated in table 'DbTest.dbo.TEST', column 'ColumnTest'. Truncated value: ‘Test truncation warnings‘'.
In a future SQL Server 2019 release, message 2628 will replace message 8152 by default.

Must declare the scalar variable with SELECT statement

I have the following statement:
DECLARE #Nr_Karton int;
SELECT #Nr_Karton = ISNULL(MAX(Nr), 1000) FROM W_Karton;
SET #Nr_Karton = #Nr_Karton + 1;
INSERT INTO W_Karton (Container_ID, Nr, Beschrieb, CreationDate, Location)
VALUES ('1', #Nr_Karton, '', getDate(), 'Bösingen');
But I get the error:
[SQL] SELECT #Nr_Karton = ISNULL(MAX(Nr), 1000) FROM W_Karton
[Err] 42000 - [SQL Server]Must declare the scalar variable "#Nr_Karton".
How to get rid of the error?
I did some playing with this. The fictional schema I created was:
CREATE TABLE W_Karton (Container_ID int, Nr int, Beschrieb varchar(1),
CreationDate datetime, Location varchar(10))
Whilst it parsed and ran fine on my local 2008R2 box, the same code did not work when pasted into a SQL Fiddle.
However, if you remove all the semi-colons apart from the last one as per this SQL Fiddle you can see it seems to work fine!
I believe it shouldn't make any difference, but if you would rather it worked and don't care about the why, give it a try...
I encountered the same issue. It turns out it is due to ';' being selected as the "Query Terminator". IN SQL Fiddle, this actually means "batch terminator". There should be a drop-down button on the bottom right that has the text "[;]". Click that and select "Keyword [GO]".

TSQL function which will raiserror if passed null?

I'm looking to add some code to my TSQL arsenal to defend against performing aggregations in SQL when the data in a column is null. Ideally there would be a SUM_NN (for sum no null), in sql server which would raiserror if any of the values were null.
Since you can't raiserror from a UDF, the only way I could think of doing it looked like this, though I don't like this solution:
CREATE FUNCTION dbo.NULL_TEST_F(#arg FLOAT)
RETURNS FLOAT
AS
BEGIN
IF(#arg IS NULL)
SELECT 1/0
RETURN #arg
END
Note: I think this is stupid solution, but I've gotten burned way too many times when I'm missing data. Also, we're using SQL Server 2005, but I'm open to 2008 and 2012 solutions. Additionally, I'd like to know how other databases deal with this type of issue.
This was my final solution:
CREATE FUNCTION [dbo].[NullTest_F]
(
#input FLOAT,
#message VARCHAR(100)
)
RETURNS FLOAT
AS
BEGIN
DECLARE #test INT
IF(#input IS NULL)
SELECT #test = CAST(#message AS INT)
RETURN #input
END
I can then embed this with a useful error message when running aggregate functions. Example:
CREATE TABLE Data (
Date DATETIME,
DataPoint FLOAT
)
INSERT INTO Data (Date, DataPoint) VALUES ('2012-03-01', 4)
INSERT INTO Data (Date, DataPoint) VALUES ('2012-03-02', 6)
SELECT SUM(NullTest_F(DataPoint, 'Missing data at' + CONVERT(VARCHAR(10), Data))
FROM Data
Maybe this one will help:
https://stackoverflow.com/a/4681815/1371070
You could create a function like suggested in the answer linked above and call it from your aggregate in case #arg is null.
It's still the same strategy overall but It's a better error to throw than divide-by-zero, I guess.