Parametrized stored procedure error - sql-server

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).

Related

Parse series of strings into WHERE IN of stored procedure

I'm trying to build an Excel tool that allows users to input a series of article IDs and query our SQL server for said IDs. Problem is that all of these IDs are strings, so I have to input them as '123', '456', '789' - which is obviously not working if I were to execute the SP via
exec SP_test #articleID = '123', '456', '789'
The SP itself is pretty basic:
ALTER PROCEDURE [dbo].[SP_test]
#articleID as VARCHAR
AS
BEGIN
SET NOCOUNT ON;
SELECT
ARTNR
, ARTBEZ
FROM table
WHERE
ARTNR IN
(#articleID)
END
GO
Like I said, this isn't working because it's not parsing '123' as '123' but rather 123.
I've then found a custom function to split strings in this thread: How to pass string parameter with `IN` operator in stored procedure SQL Server 2008
If I then just execute the query itself (not as a SP), it's actually giving me the proper results:
SELECT
ARTNR
, ARTBEZ
FROM table
WHERE
ARTNR IN
(select value from fn_split('123, 456, 789', ','))
But if I then try to turn that into my SP a la
ALTER PROCEDURE [dbo].[SP_test]
#articleID as VARCHAR
AS
BEGIN
SET NOCOUNT ON;
SELECT
ARTNR
, ARTBEZ
FROM table
WHERE
ARTNR IN
(select value from fn_split(#articleID, ','))
END
GO
it seems to again be parsing the articleIDs incorrectly.
Can anyone clue me in on how to get this to work properly? I've seen the string_split function being thrown around in this context, but m server's compatibility level isn't high enough for that.

How can I save a stored procedure which contains errors in SQL Server?

I want to save a stored procedure which contains errors according to SQL Server.
This is the procedure code:
Create PROCEDURE [Product].[JewelSearch]
#JewelItem bigint,
#JewelType nvarchar(50),
#JewelMate nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM Product.#JewelType
WHERE Material = #JewelMate OR Item# = #JewelItem;
END
The problem is that I have a Product schema, and I am taking the table name from my main application and saving it in #JewelType and in each search in main application the table name must be changed and each time their will be a different table name in #JewelType.
According to me the query is perfect but SQL Server does not allow me to execute it and save it. Is there a way that I can forcibly save this stored procedure? Hope you understand my question please help me if possible.
If it is SQL Server, something like this should work
Create PROCEDURE [Product].[JewelSearch]
#JewelItem bigint,
#JewelType nvarchar(50),
#JewelMate nvarchar(50),
#SQL nvarchar(max)
AS BEGIN
SET NOCOUNT ON;
SET #SQL = 'Select * From Product.'+#JewelType+' where Material = '+#JewelMate+' OR Item# = '+CAST(#JewelItem as nvarchar(50))+'; '
EXEC(#SQL)
END
This is untested as I am on my Mac, but you get the idea.
If you are going to use this, be aware of the dangers of dynamic SQL in relation to SQL Injection.
SQL Injection with Dynamic SQL - MSDN

SSIS has validation error on SQL Server stored procedure

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.

How do I force nvarchar input width in SQL Server?

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

EF4 - The selected stored procedure returns no columns

I have query in a stored procedure that calls some linked servers with some dynamic SQL. I understand that EF doesn't like that, so I specifically listed all the columns that would be returned. Yet, it still doesn't like that. What am I doing wrong here? I just want EF to be able to detect the columns returned from the stored procedure so I can create the classes I need.
Please see the following code that makes up the last lines of my stored procedure:
SELECT
#TempMain.ID,
#TempMain.Class_Data,
#TempMain.Web_Store_Class1,
#TempMain.Web_Store_Class2,
#TempMain.Web_Store_Status,
#TempMain.Cur_1pc_Cat51_Price,
#TempMain.Cur_1pc_Cat52_Price,
#TempMain.Cur_1pc_Cat61_Price,
#TempMain.Cur_1pc_Cat62_Price,
#TempMain.Cur_1pc_Cat63_Price,
#TempMain.Flat_Length,
#TempMain.Flat_Width,
#TempMain.Item_Height,
#TempMain.Item_Weight,
#TempMain.Um,
#TempMain.Lead_Time_Code,
#TempMain.Wp_Image_Nme,
#TempMain.Wp_Mod_Dte,
#TempMain.Catalog_Price_Chg_Dt,
#TempMain.Description,
#TempMain.Supersede_Ctl,
#TempMain.Supersede_Pn,
TempDesc.Cust_Desc,
TempMfgr.Mfgr_Item_Nbr,
TempMfgr.Mfgr_Name,
TempMfgr.Vendor_ID
FROM
#TempMain
LEFT JOIN TempDesc ON #TempMain.ID = TempDesc.ID
LEFT JOIN TempMfgr ON #TempMain.ID = TempMfgr.ID
EF doesn't support importing stored procedures which build result set from:
Dynamic queries
Temporary tables
The reason is that to import the procedure EF must execute it. Such operation can be dangerous because it can trigger some changes in the database. Because of that EF uses special SQL command before it executes the stored procedure:
SET FMTONLY ON
By executing this command stored procedure will return only "metadata" about columns in its result set and it will not execute its logic. But because the logic wasn't executed there is no temporary table (or built dynamic query) so metadata contains nothing.
You have two choices (except the one which requires re-writing your stored procedure to not use these features):
Define the returned complex type manually (I guess it should work)
Use a hack and just for adding the stored procedure put at its beginning SET FMTONLY OFF. This will allow rest of your SP's code to execute in normal way. Just make sure that your SP doesn't modify any data because these modifications will be executed during import! After successful import remove that hack.
Adding this Non-Logical block of code solved the problem. Even though it will never Hit
IF 1=0 BEGIN
SET FMTONLY OFF
END
Why does my typed dataset not like temporary tables?
http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataset/thread/fe76d511-64a8-436d-9c16-6d09ecf436ea/
Or you can create a User-Defined Table Type and return that.
CREATE TYPE T1 AS TABLE
( ID bigint NOT NULL
,Field1 varchar(max) COLLATE Latin1_General_CI_AI NOT NULL
,Field2 bit NOT NULL
,Field3 varchar(500) NOT NULL
);
GO
Then in the procedure:
DECLARE #tempTable dbo.T1
INSERT #tempTable (ID, Field1, Field2, Field3)
SELECT .....
....
SELECT * FROM #tempTable
Now EF should be able to recognize the returned columns type.
As some others have noted, make sure the procedure actually runs. In particular, in my case, I was running the procedure happily without error in SQL Server Management Studio completely forgetting that I was logged in with admin rights. As soon as I tried running the procedure using my application's principal user I found there was a table in the query that that user did not have permission to access.
Interesting side note: Had the same problem which I first solved by using Table Variables, rather than Temp Tables (just for the import). That wasn't particularly intuitive to me, and threw me off when initially observing my two SProcs: one using Temp tables and one with Table Variables.
(SET FMTONLY OFF never worked for me, so I just changed my SProcs temporarily to get the column info, rather than bothering with the hack on the EF side just as an FYI.)
My best option was really just manually creating the complex type and mapping the function import to it. Worked great, and the only difference ended up being that an additional FactoryMethod to create the properties was included in the Designer.
What I would add is:
That the import also fails if the stored procedures has parameters and returns no result set for the default parameter values.
My stored procedure had 2 float parameters and would not return anything when both parameters are 0.
So in order to add this stored procedure to the entity model, I set the value of these parameters in the stored procedure so that it is guaranteed to return some rows, no matter what the parameters actually are.
Then after adding this stored procedure to the entity model I undid the changes.
both solutions :
1- Define the returned complex type manually (I guess it should work)
2- Use a hack and just for adding the stored procedure put at its beginning SET FMTONLY OFF.
not working with me in some procedure however it worked with other one!
my procedure ends with this line:
SELECT machineId, production [AProduction]
, (select production FROM #ShiftBFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [BProduction]
, (select production FROM #ShiftCFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [CProduction]
FROM #ShiftAFinalProd
ORDER BY machineId
Thanks
In addition to what #tmanthley said, be sure that your stored procedure actually works by running it first in SSMS. I had imported some stored procedures and forgot about a couple dependent scalar functions, which caused EF to determine that the procedure returned no columns. Seems like a mistake I should have caught earlier on, but EF doesn't give you an error message in that case.
Entity Framework will try to get the columns by executing your stored procedure, passing NULL for every argument.
Please make sure that the stored procedure will return something under all the circumstances. Note it may have been smarter for Entity Framework to execute the stored proc with default values for the arguments, as opposed to NULLs.
ER does the following to get the metadata of the table:
SET FMTONLY ON
This will break your stored procedure in various circumstances, in particular, if it uses a temporary table.
So to get a result as complex type; please try by adding
SET FMTONLY OFF;
This worked for me - hope it works for you too.
Referred from https://social.msdn.microsoft.com/Forums/en-US/e7f598a2-6827-4b27-a09d-aefe733b48e6/entity-model-add-function-import-stored-procedure-returns-no-columns?forum=adodotnetentityframework
In my case adding SET NOCOUNT ON; at the top of the procedure fixed the problem. It's best practice anyway.
In my case SET FMTONLY OFF did not work. The method I followed is, I took backup of original stored procedure and replace with only column name like the below query.
Select Convert(max,'') as Id,Convert(max,'') as Name
After this change, create new function import, complex type in entity framework.
Once the function import and complex type is created, replace the above query with your original stored procedure.
SET FMTONLY OFF
worked for me for one of the procedure but failed for other procedure. Following steps helps me to resolve my problem
Within a stored procedure, I have created temporary table with the same column type and inserted all the data returned by dynamic query to temp table.
and selected the temp table data.
Create table #temp
(
-- columns with same types as dynamic query
)
EXEC sp_executeSQL #sql
insert into #temp
Select * from #temp
drop table #temp
Deleted existing complex type, import function and stored procedure instance for old stored procedure and updated entity model for current new procedure.
Edit the imported Function in entity modal for desired complex type, you will get all the column information there which is not getting for previous stored procedure.
once you have done with the type creation you can delete the temporary table from stored procedure and then refresh Entity Framework.
In Entity framework, while getting column information the sql executes the procedure with passing null values in parameter. So I handled null case differently by creating a temp table with all the required columns and returning all the columns with no value when null is passed to the procedure.
In my procedure there was dynamic query, something like
declare #category_id int
set #category_id = (SELECT CATEGORY_ID FROM CORE_USER where USER_ID = #USER_ID)
declare #tableName varchar(15)
declare #sql VARCHAR(max)
declare #USER_IDT varchar(100)
declare #SESSION_IDT varchar(10)
IF (#category_id = 3)
set #tableName = 'STUD_STUDENT'
else if(#category_id = 4)
set #tableName = 'STUD_GUARDIAN'
if isnull(#tableName,'')<>''
begin
set #sql = 'SELECT [USER_ID], [FIRST_NAME], SCHOOL_NAME, SOCIETY_NAME, SCHOOL_ID,
SESSION_ID, [START_DATE], [END_DATE]
from #tableName
....
EXECUTE (#sql)
END
ELSE
BEGIN
SELECT * from #UserPrfTemp
END
I was not getting the column information in
my case after using the set FMTONLY OFF trick.
This is temp table I created to get the blank data.
Now I am getting the column info
Create table #UserPrfTemp
(
[USER_ID] bigint,
[FIRST_NAME] nvarchar(60),
SCHOOL_NAME nvarchar(60),
SOCIETY_NAME nvarchar(200)
.....
}
I solved this problem creating a table variable and then returning from it.
DECLARE #VarTable TABLE (
NeededColumn1 VARCHAR(100),
NeededColumn2 INT,
NeededColumn3 VARCHAR(100)
)
...
--Fetch Data from Linked server here
...
INSERT INTO #VarTable (NeededColumn1,NeededColumn2,NeededColumn3)
SELECT Column1, Column2, Column3
FROM #TempTable
SELECT * FROM #VarTable.
In that manner, your the SP result will be bounded to the table variable, which EF has access to.
I discovered a method that should help most people out whatever's happening.
Pull up your favourite SQL client and run the proc that you're trying to update with every parameter = null. Visual Studio is literally trying to do this when SET FMTONLY ON. Run a trace. You'll see.
You'll probably get an error, or unexpected data out. Fix that and your issue is fixed.
In my case the function read in JSON and failed because the JSON string was empty.
I just put something like
IF(#FooJSON IS NULL)
BEGIN
SELECT 1 VAR1, 2 VAR2;
END
ELSE
--OTHER LOGIC
That's probably an ugly solution, but I inherited this mess and we don't go into Ravenholm.
Change #Temp tables with WITH SQL EXPRESSION

Resources