I’ve got a script that I’ve created for our production line where the user enters some variables into the script before executing. The Problem is that the variables are NVARCHAR(9) and if the user inputs a 10 character sting the last character is cut off (as expected) what I want to know is how can I have SQL throw an error if they enter a value that is too long? This issue stems from users fat fingering their inputs.
Example:
Valid input -
DECLARE #ClientCode NVARCHAR(9)
SET #ClientCode = N'ABCDEFGHI'
SELECT #ClientCode
Results
ABCDEFGHI
Invalid input –
DECLARE #ClientCode NVARCHAR(9)
SET #ClientCode = 'ABCDDEFGHI'
SELECT #ClientCode
Results
ABCDDEFGH
What I’m hoping for is a setting that will have SSMS raise an error. What I’m hoping to avoid is something like -
DECLARE #ClientCode NVARCHAR(50)
...
IF LEN(#ClientCode) > 9
RAISERROR('Too long dummy.',16,1)
Thanks for you help
Please see SQL Server silently truncates varchar's in stored procedures - SQL Server cannot be set to automatically raise truncation errors for inserts inside of a stored procedure, so you have to perform the check yourself. You can do this in the stored procedure (the code you listed as "hoping to avoid") or you can validate beforehand in the client application.
I've also been looking at the question Scott Chapman references in his answer, however, I found igorp's answer midway down interesting, I had to hack it a bit to fix the SQL but it can work:
declare #p1 varchar(max), #p2 varchar(max)
select #p1 = 'abcd'
declare #p1Int varchar(2), #p2Int varchar(3)
declare #test table (p1 varchar(2), p2 varchar(3))
insert into #test (p1,p2) values (#p1, #p2)
select #p1Int=p1, #p2Int=p2 from #test
Related
At the company I work there are a range of stored procedures which take a user-defined table as an input parameter and is it happens this is not always needed for the procedure being used. In SSMS if write an Execute statement without defining the UDT I get the error line with a tooltip telling me the procedure expects the table to be provided, but if I try to run without providing the table it works just fine.
This makes me assume these are optional parameters even though they don't have a clear default like other input parameters would need? If that's the case how can you force them to be non-optional?
As it is for my company procedures the non-optional nature is preferable but I'd like to know why this is as a learning point and how to get around it please.
This SQL demonstrates my question:
CREATE TYPE Dummy_Table AS TABLE (ID INT, Name VARCHAR(50));
GO
CREATE PROCEDURE Dummy_Procedure #Mode VARCHAR(50), #Dummy_Table Dummy_Table READONLY
AS
BEGIN
SELECT #Mode
SELECT * FROM #Dummy_Table
END
GO
EXEC Dummy_Procedure #Mode = 'Dummy_Mode'
TVPs are optional, and they can produce some weird behavior if you are expecting records and don't get any. You could include some logic to check if there are any records in the table.
This article also helps:
https://msdn.microsoft.com/en-us/library/bb510489.aspx
CREATE TYPE Dummy_Table AS TABLE (ID INT, Name VARCHAR(50));
GO
ALTER PROCEDURE Dummy_Procedure #Mode VARCHAR(50), #Dummy_Table Dummy_Table READONLY
AS
BEGIN
SELECT #Mode
declare #test int
SELECT #test = COUNT(*) FROM #Dummy_Table
if #test = 0
select 'stop'
else
select 'continue'
END
GO
I'm trying to call a custom stored procedure in SQL Server 2008 R2 from SSIS in Visual Studio 2012. I wrote and tested the stored procedure in SSMS 2012 and it works as expected.
However, when I try to place it in an OLE DB Command component I receive a Divide by 0 error when I refresh the component or when the SSIS package validates.
Here's the code for the stored procedure:
CREATE PROCEDURE [ldg].[2015HRUpdate(TEST)]
#Employee varchar(20), -- maps to EM.Employee, primary key
#Title varchar(50), -- maps to EM.Title
#PayRate varchar(50) = '0', -- maps to EM.JobCostRate, convert to decimal
-- #Percentage Decimal(19,4) = 0, -- workaround
#OldPayRate Decimal(19,4) = 0, -- used to calculate Employees_SalaryHistory.Custnprcent, convert to decimal
#LaborCategory varchar(50) = '0', -- maps to EM.BillingCategory, convert to small int
#EmployeeDesignation varchar(50), -- maps to EmployeeCustomTabFields.CustEmployeeDesg
#FSLAStatus varchar(50), -- maps to EmployeeCustomTabFields.CustFSLAStatus
#Supervisor varchar(20), -- maps to EM.Supervisor
#SupervisorName varchar(255), -- maps to Employees_SalaryHistory.custSuper
#ModUser nvarchar(20),
#ModDate datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Convert data types to match database data types
declare #JobCostRate decimal(19,4);
declare #OldJobCostRate decimal(19,4);
declare #BillingCategory smallint;
declare #Percent decimal(19,4);
if #PayRate is null or #PayRate = ''
set #PayRate = '0';
set #JobCostRate = CONVERT(decimal(19,4), #PayRate);
set #OldJobCostRate = #OldPayRate;
/* this works in T-SQL but when SSIS tries to validate I get a div/0 error */
if #OldJobCostRate != 0
begin
set #Percent = ((#JobCostRate - #OldJobCostRate)/#OldJobCostRate) * 100; --errors out right here with a divide by 0 error.
--set #Percent = 0;
end
else
begin
set #Percent = 0;
end
set #BillingCategory = CONVERT(smallint, #LaborCategory);
-- SQL statements for procedure here
-- Update EM table
-- Update EmployeeCustomTabFields table
-- Insert into Salary history table
END
GO
I have placed a comment on the line that produces the error. If I comment that line out and uncomment the one below it SSIS will validate the procedure without issue.
I finally worked around the issue by creating a derived field in the ETL but I would like to know why SSIS/OLE-DB is causing this issue for the next time it pops up.
Thanks,
Roy
If you alter your procedure to look like
SET NOCOUNT ON;
-- This is a bloody hack to get SSIS to be happy about metadata.
IF 1=2
BEGIN
SELECT 1 AS StupidHackery;
END
I believe you'll get around this issue. The root cause is that SSIS wants to validate the metadata from the proc and doesn't actually evaluate the logic in there. I don't have any definitive resources on the matter, pity, but for me at least, I could recreate your issue and by using this stupid hack, get around it. I've had to use the same thing when dealing with temporary tables.
i created a stored procedure on sql server for updating records,while there it works fine when I insert the required parameters... but when it comes to ASP .NET when I run the application when I press update on ASPX GridView it gives me a Message
"Procedure or function custUPDATE has too many arguments specified."
here is the code for my procedure
alter proc custUPDATE
( #odid int,
#customer_id int,
#priceID int,
#etAMOUNT int,
#amntPaid decimal(18,2),
#od_status varchar(20),
#py_status varchar(20),
#order_date smalldatetime
-- #dummy varchar(30) =null
)
as
begin
set nocount on;
declare #amnt_paid decimal(18,2);
declare #rmn decimal(23,4);
declare #tt money;
select #amnt_paid=eggsOrders.amnt_paid from eggsOrders where od_ID=#odid;
select #rmn= orderVIEW.Remaining from orderVIEW where od_ID=#odid;
select #tt=orderVIEW.Total from orderVIEW where od_ID=#odid;
--select #amnt_paid= amnt_paid from inserted;
if(#amnt_paid=#tt)
begin
update [dbo].[eggsOrders] set customer_ID=#customer_id, price_ID=#priceID, ET_amount=#etAMOUNT, amnt_paid=#amntPaid, Od_status=#od_status, py_status='paid in full', order_date=#order_date where od_ID=#odid;
end
else if(#amnt_paid>0 and #amnt_paid!=#tt)
begin
update [dbo].[eggsOrders] set customer_ID=#customer_id, price_ID=#priceID, ET_amount=#etAMOUNT, amnt_paid=#amntPaid, Od_status=#od_status, py_status='In-Progress', order_date=#order_date where od_ID=#odid
end
else if(#amnt_paid=0 and #rmn =#tt)
begin
update [dbo].[eggsOrders] set customer_ID=#customer_id, price_ID=#priceID, ET_amount=#etAMOUNT, amnt_paid=#amntPaid, Od_status=#od_status, py_status='Payment Pending', order_date=#order_date where od_ID=#odid
end
end
go
what am I doing wrong???
please help
The error is cristal clear: you're passing more parameters to the method than what it expect, causing the error. Review carefully how many parameters are you passing in the call to SP.
I’ve noticed occasionally that ASP.NET will cache the old SPROC in Visual Studio even after a change is made on SQL Server. So for example you changed custUPDATE by adding a parameter, and also added the parameter to your ASP.NET code, but are still receiving the “too many arguemtns specified” error because the old SPROC is being cached.
If this is the case I would try the following:
Change the SPROC name in your ASP.NET page from custUPDATE to [custUPDATE] and then try running it.
I am using SQL Server 2005 Management Studio Express. Coins and themes are my tables. I created a stored procedures using the above two and got struck with
Error:Msg 102, Level 15, State 1, Procedure themestat, Line 1
Incorrect syntax near 'id2'.
Here is my whole procedure:
create procedure themestat(id2 In numeric, id1 In numeric)
is
#userid nvarchar(50), #co nvarchar(50), #price nvarchar(50)
begin
update themes set prioirty=1 where themeid=id2;
select credits as co from coins where uid=id1;
select rate as price from themes where priority=1;
if(co>price)
begin
update themes set status=1 where priority=1;
update themes set priority=0 where themeid=id2;
end
else
begin
update themes set priority=0 where theme=id2;
PRINT 'no sufficient coins'
end
end
I am curious to know where I went wrong ??
I'm not sure where you've got the syntax from, but datatypes are declared as '#param type', so the first line should read:
create procedure themestat
#id2 numeric
#id1 numeric
Then obviously change all references of id1 and id2 as appropriate. There's other syntax errors in the script (missing declare, is instead of as, possibly others - I've not looked much closer).
This makes me wonder whether you've come from a different SQL dialect? I suggst reading about CREATE PROCEDURE on the MSDN (as well as other pages).
I recently encountered an issue while porting an app to SQL Server. It turned out that this issue was caused by a stored procedure parameter being declared too short for the data being passed to it: the parameter was declared as VARCHAR(100) but in one case was being passed more than 100 characters of data. What surprised me was that SQL Server didn't report any errors or warnings -- it just silently truncated the data to 100 characters.
The following SQLCMD session demonstrates this:
1> create procedure WhereHasMyDataGone (#data varchar(5)) as
2> begin
3> print 'Your data is ''' + #data + '''.';
4> end;
5> go
1> exec WhereHasMyDataGone '123456789';
2> go
Your data is '12345'.
Local variables also exhibit the same behaviour:
1> declare #s varchar(5) = '123456789';
2> print #s;
3> go
12345
Is there an option I can enable to have SQL Server report errors (or at least warnings) in such situations? Or should I just declare all local variables and stored procedure parameters as VARCHAR(MAX) or NVARCHAR(MAX)?
SQL Server has no such option. You will either have to manually check the length of strings in your stored procedure and somehow handle the longer strings or use the nvarchar(max) option. If disk space isn't an issue then the nvarchar(max) option is certainly the easiest and quickest solution.
You don't have to use nvarchar(max) just use nvarchar(length+1) [e.g. if your column length is 50 then you would set the parameter to be nvarchar(51)]. See the answer from DavidHyogo - SQL Server silently truncates varchar's in stored procedures.
I don't know of a way to make the server do it, but I've been using the SQL Server Projects feature of Visual Studio Team System Developer Edition. It includes code analysis which caught a truncation problem of mine: using an int parameter to insert into a smallint column.
Though awkward, you can, however, dynamically check for parameter length before a call, e.g.
CREATE FUNCTION MyFunction(#MyParameter varchar(10))
RETURNS int
AS
BEGIN
RETURN LEN(#MyParameter)
END
GO
DECLARE #MyValue varchar(15) = '123456789012345'
DECLARE #ParameterMaxLength int
SELECT #ParameterMaxLength = CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'dbo' AND
SPECIFIC_name = 'MyFunction' AND
PARAMETER_NAME = '#MyParameter'
IF #ParameterMaxLength <> -1 AND
LEN(#MyValue) > #ParameterMaxLength
PRINT 'It''s too looooooooooooooooooong'
I omitted the called function's database name in the query and in the reference to INFORMATION_SCHEMA.PARAMETERS to ensure that my sample would run without edits.
I don't necessarily advocate this, but I wanted to point out that the information may be available to detect imminent truncation dynamically, if in some critical situation this is needed.
You can use LEFT in SQL and specified the length that you want to insert.
for example.
CREATE TABLE Table1
(
test varchar(10)
)
insert into Table1 values (LEFT('abcdefghijklmnopqrstuvwxyz',10))
This will insert only
abcdefghij on table