msg 8152 level 16 state 2 string or binary data would be truncated - sql

I am getting this error while executing a procedure
Msg 8152, Level 16, State 2, Procedure SP_xxxxx, Line 92 String or
binary data would be truncated.
I have created a temp table which I will load the data from main table once in procedure and I would be using this table not the main table as main table has huge volume of data and many unnecessary columns.
When I run the below code from sql server management studio then there is no error but when I run this code from a procedure then their is the above error message.
Insert into abc_TMP // tmp for procedure with required columns
Select
Item,
Description,
size,
qty,
stock,
Time ,
Measure
from abc // main table has many columns

One way to check this issue, is to see length of each value
Assume you have table like below
create table t
(
col1 varchar(10),
col2 varchar(10)
)
Inserts into the table will fail,if you try to insert more than 10 characters,if you try to insert them in a batch, you will not get offending value.
So you need to check its length like below , prior to insert
;with cte
as
(
select
len(col1) as col1,
len(Col2) as col2
from table
)
select * from cte where col2>10
There has been number of requests raised with Microsoft to enhance error message and they have finally fixed this issue in SQL2019.
Now you can get the exact value causing the issue
References:
https://voiceofthedba.com/2018/09/26/no-more-mysterious-truncation/

I suspect that you are looking at the wrong line of code in the stored proc.
When you open the proc, i.e. ALTER... you have a header on the stored proc that will throw the line number out.
If you run this, replacing proc_name with your procedure name:
sp_helptext proc_name
That will give you the code that the procedure will actually run, with accurate line numbers if you paste it into a new window.
Then you'll see where the actual error is happening.
If you want a simple way to prove this theory, put a bunch of Print 'some sql 1', Print 'some sql 2' lines in around the code you think is causing the error and see what is output when the error is thrown.

Related

SQL Server stored procedure: how to return column names/values of type failures in variable?

Ambiguous thread name, I apologize. I am not new to SQL, but I'm new to coding longer stored procedures so I don't deal with variables much outside of passing through maybe a table name or returning row count, etc.
I have a stored procedure that is executing an insert from a staging table to a fact table. There are a couple type casts in the insert.
If the insert fails due to a typecast. Is there any way to return the name of the column that failed, along with what the failed value was? How would I code that? I know that Try_parse would make it so the stored procedure doesn't fail on type cast failure, but I want to be able to pass back exactly what column and value failed.
I show an example here:
Create Procedure dbo.Example_Insert
#updateUser varchar(255)
As
Begin
Insert Into dbo.Energy_Costs (Energy_Cost_Id, Project_Id, Propane_Cost_Dollars,
Electricity_Cost_Dollars, Fuel_Savings_Evaluator)
Select
Next Value For energy_cost_id,
r.project_id,
Cast(r.propane_cost_dollars As Decimal(18,2)),
Cast(r.electricity_cost_dollars As Decimal(18,2)),
#update_user fuel_savings_evaluator
From
staging_table r
return ##ROWCOUNT
end
You can use CURSOR in sql then insert one line at a time. When insert fail return value currently row error.
I hope my idea suitable with you.

How to create a stored procedure to copy data from a query to a temporary table?

I have need of inserting data to a temporary table from an existing table/query. The following produces the error detailed below.
CREATE TABLE SPTemporary
AS
BEGIN
SELECT * into #temppT
FROM SampleTable
END
Throws this error:
Msg 156, Level 15, State 1, Line 3
Incorrect syntax near the keyword 'begin'.
Correct your syntax, use procedure instead of table :
create procedure SPTemporay
as
begin
select * into #temppT
from SampleTable
end
However, if you want only copy of data then only subquery is enough :
select st.* into #temppT
from SampleTable st
One method is:
select st.*
into SPTemporay
from SampleTable st
One select can only put data in one table. It is unclear which one you really want SPTemporary or #temppT. You can repeat the select if you really need the same data in two tables.
EDIT:
If you want a stored procedure, you could do:
create procedure SPTemporay
as begin
select *
into #temppT
from SampleTable
end;
This is rather non-sensical, because the temporary table is discarded when the stored procedure returns.
I think the syntax is wrong, it should be like that:
create table SPTemporay
as
select * from SampleTable
I hope this helps.

Early execution of "sp_rename" causes query to fail

I'm having a strange problem with an MSSQL Query that I'm trying to run in Microsoft SQL Server 2014. It is an update script for my database structure. It should basically rename a Column (from Price to SellingPrice) of a Table after its content was merged to another one.
USE db_meta
GO
DECLARE #BakItemPrices TABLE
(
ItemNum int,
Price int,
CashPrice int
)
-- backup old prices
insert into #BakItemPrices
select ItemNum, Price from dbo.ItemInfo
-- merge into other table
alter table ShopInfo
add column Price int NOT NULL DEFAULT ((0))
update ShopInfo
set ShopInfo.Price = i.Price
from ShopInfo s
inner join #BakItemPrices i
on s.ItemNum = i.ItemNum
GO
-- rename the column
exec sp_rename 'ItemInfo.Price', 'SellingPrice', 'COLUMN' -- The Debugger executes this first
GO
This query always gave me the error
Msg 207, Level 16, State 1, Line 13
Invalid column name 'Price'.
I couldn't understand this error until I debugged the query. I was amazed as I saw that the debugger wont even hit the breakpoint I placed at the backup code and says that "its unreachable because another batch is being executed at the moment".
Looking further down I saw that the debugger instantly starts with the exec sp_rename ... line before it executes the query code that I wrote above. So at the point my backup code is being executed the Column is named SellingPrice and not Price which obviously causes it to fail.
I thought queries get processed from top to bottom? Why is the execute sequence being executed before the code that I wrote above?
Script is sequenced from top to down. But some changes to schema is "visible" after the transaction with script is committed. Split your script into two scripts, it can help.

Sql select insert debugging

I would like to find a quick method for debugging a insert-select statement.
Example:
Create table tbl_int (
tabid int identity,
col1 bigint)
Create table tbl_char(
tabid int identity,
col2 nvarchar(255))
insert into tbl_char(col2)
select '1' union
select '2' union
select 'a'
insert into tbl_int(col1)
select col2
from tbl_char
Of course, the insert select above fails to run and it is obvious that 'a' cannot be converted to bigint. But what happens when I have 1 milion records in tbl_char. Is there any way of finding the source value of the error:
"Error converting data type nvarchar to bigint."
P.s. Using a convert or cast function and scanning the table with top until finding the right value is a little bit too expensive.
Why don't you wrap the SQL that throw the exception into a Try/Catch block to have more info about it
BEGIN TRY
SELECT *
FROM sys.messages
WHERE message_id = 21;
END TRY
GO
-- The previous GO breaks the script into two batches,
-- generating syntax errors. The script runs if this GO
-- is removed.
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber;
END CATCH;
GO
the below are all the error information you can check
ERROR_NUMBER() returns the error number.
ERROR_MESSAGE() returns the complete text of the error message. The text includes the values supplied for any substitutable parameters such as lengths, object names, or times.
ERROR_SEVERITY() returns the error severity.
ERROR_STATE() returns the error state number.
ERROR_LINE() returns the line number inside the routine that caused the error.
ERROR_PROCEDURE() returns the name of the stored procedure or trigger where the error occurred.
http://msdn.microsoft.com/en-us/library/ms179296.aspx
if this is not enough you can even write a query to select all the records where certain field is not convertible to a number(in your case there is a NVarChar which is not convertible)
The following example uses ISNUMERIC to return all the postal codes that are not numeric values.
SELECT City, PostalCode
FROM Person.Address
WHERE ISNUMERIC(PostalCode)<> 1
http://msdn.microsoft.com/en-us/library/ms186272.aspx
Let's start with why are you trying to send data that is not the same data type to another table? If you do not want non integer data in the column why are you allowing it to be string data to begin with? So first you need to look at if you have a design issue that is affecting data integrity.
If you are doing imports from some other system and have no control over the data type the use, then you need to clean the data before doing the insert. There is no one size fits all fix for this. You will have to write code to find all data that won't meet the terms of the table you are moving it to datatype by data type and field by field. This may be much more complex than using isnumeric (which can have false positives especially if there is decimal information in there as well.) and isdate() and you may need to write functions specific to your needs. It can take a long time to get the cleaning correct. You might need to restrict the values to a certain subset or have a conversion table that converts what they put inthe table to what your system will accept. Suggest you identify the bad data first, move it to an exception table and then do the insert based on records not in the exception table.
As you understand, the char value converted into bigint when you execute insert..select.
So, we cannot avoid the converting, but we can try to avoid the second table scanning.
I propose to create INSTEAD OF INSERT trigger for table tbl_int.
In the body of this trigger you can convert the char value into bigint, and if you recieve the error, you can insert this char value into staging table. If there is no error when convering, you can insert this bigint value into your tbl_int table.

Stored Procedure consist Add column, Update data for that column, and Select all data from that table

I've written a stored procedure as following:
CREATE PROC spSoNguoiThan
#SNT int
AS
begin
IF not exists (select column_name from INFORMATION_SCHEMA.columns where
table_name = 'NhanVien' and column_name = 'SoNguoiThan')
ALTER TABLE NhanVien ADD SoNguoiThan int
else
begin
UPDATE NhanVien
SET NhanVien.SoNguoiThan = (SELECT Count(MaNguoiThan)FROM NguoiThan
WHERE MaNV=NhanVien.MaNV
GROUP BY NhanVien.MaNV)
end
SELECT *
FROM NhanVien
WHERE SoNguoiThan>#SNT
end
GO
Then I get the error :
Server: Msg 207, Level 16, State 1, Procedure spSoNguoiThan, Line 12
Invalid column name 'SoNguoiThan'.
Server: Msg 207, Level 16, State 1, Procedure spSoNguoiThan, Line 15
Invalid column name 'SoNguoiThan'.
Who can help me?
Thanks!
When the stored proc is parsed during CREATE the column does not exist so you get an error.
Running the internal code line by line works because they are separate. The 2nd batch (UPDATE) runs because the column exists.
The only way around this would be to use dynamic SQL for the update and select so it's not parsed until EXECUTE time (not CREATE time like now).
However, this is something I really would not do: DDL and DML in the same bit of code
I ran into this same issue and found that in addition to using dynamic sql I could solve it by cross joining to a temp table that had only one row. That caused the script compiler to not try to resolve the renamed column at compile time. Below is an example of what I did to solve the issue without using dynamic SQL
select '1' as SomeText into #dummytable
update q set q.ValueTXT = convert(varchar(255), q.ValueTXTTMP) from [dbo].[SomeImportantTable] q cross join #dummytable p