Stored Procedure giving error - sql

I'm trying to use a stored procedure to display the results of a table. The stored procedure is giving the error 'Procedure expects parameter '#parameters' of type 'ntext/nchar/nvarchar'
ALTER PROCEDURE COMNODE_PROC_SearchProduct --'','GUN',''
#PRODUCTID INT = NULL,
#PRODUCT_NAME VARCHAR(500) = NULL,
#PRODUCT_POINTS INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #SQLQuery AS NVarchar(MAX)
Declare #ParamDefinition AS NVarchar(MAX)
Set #ParamDefinition = '#ID INT,
#NAME VARCHAR(500),
#POINTS INT'
Set #SQLQuery = 'SELECT PRODUCT_ID,PRODUCT_NAME,PRODUCT_REDEEM_POINTS FROM TBL_REDEEM_PRODUCT WHERE (1 = 1)';
If #PRODUCTID Is Not Null
Set #SQLQuery = #SQLQuery + ' And (PRODUCT_ID ='+CAST(#PRODUCTID AS VARCHAR(500) )
If #PRODUCT_NAME Is Not Null
Set #SQLQuery = #SQLQuery + ' And (PRODUCT_NAME =' + CAST(#PRODUCT_NAME AS VARCHAR(500) )
If #PRODUCT_POINTS Is Not Null
Set #SQLQuery = #SQLQuery + ' And (PRODUCT_REDEEM_POINTS ='+ CAST(#PRODUCT_POINTS AS VARCHAR(500))
Execute sp_Executesql #SQLQuery,
#ID = #PRODUCTID ,
#NAME = #PRODUCT_NAME ,
#POINTS = #PRODUCT_POINTS;
END

You need to pass in the parameter definition to sp_executesql, see: https://msdn.microsoft.com/en-us/library/ms188001.aspx
Execute sp_Executesql #SQLQuery,
#ParamDefinition,
#ID = #PRODUCTID ,
#NAME = #PRODUCT_NAME ,
#POINTS = #PRODUCT_POINTS;

One of the main reasons you would want to use sp_executesql so do not have to concatenate variables, have you can be protected from sql-injection attack using the parameterised query.
You concatenating parameters just kill the purpose and makes your query vulnerable to sql-injection . See below the proper use of dynamic sql the safe way.
ALTER PROCEDURE COMNODE_PROC_SearchProduct --'','GUN',''
#PRODUCTID INT = NULL,
#PRODUCT_NAME VARCHAR(500) = NULL,
#PRODUCT_POINTS INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #SQLQuery AS NVarchar(MAX);
Declare #ParamDefinition AS NVarchar(MAX);
Set #ParamDefinition = N'#ID INT, #NAME VARCHAR(500), #POINTS INT';
-- A much cleaner way to write this would be...
Set #SQLQuery = N'SELECT PRODUCT_ID,PRODUCT_NAME,PRODUCT_REDEEM_POINTS
FROM TBL_REDEEM_PRODUCT
WHERE (1 = 1)'
+ CASE WHEN #PRODUCTID Is Not Null
THEN N' And PRODUCT_ID = #ID ' ELSE N' ' END
+ CASE WHEN #PRODUCT_NAME Is Not Null
THEN N' And PRODUCT_NAME = #NAME ' ELSE N' ' END
+ CASE WHEN #PRODUCT_POINTS Is Not Null
THEN N' And PRODUCT_REDEEM_POINTS = #POINTS' ELSE N' ' END
Execute sp_Executesql #SQLQuery
,#ParamDefinition --<-- this was missing
,#ID = #PRODUCTID
,#NAME = #PRODUCT_NAME
,#POINTS = #PRODUCT_POINTS;
END

Related

SQL Server Default Column value from function

I want to set default value to Id column in Person table with a function
that goes like this:
Function:
IF OBJECT_ID ( 'GetLastId','FN') IS NOT NULL
DROP function GetLastId;
GO
CREATE FUNCTION [dbo].[GetLastId]
(#TableName nvarchar(max))
RETURNS int
AS
BEGIN
DECLARE #LastId int;
DECLARE #sql nvarchar(max);
SET #sql = 'SELECT #LastId = ISNULL(MAX(Id), 0) + 1 FROM ' + #TableName + ';'
EXECUTE sp_executesql #sql, N'#LastId int output', #LastId = #LastId output;
RETURN #LastId
END
and then:
UPDATE Person
SET Id = dbo.GetLastId('Person')
Running this code throws an error:
Only functions and some extended stored procedures can be executed from within a function.
So how to fix this and make it work as a default value?
And please do not say "Use triggers..." as I intend to use it with Entity Framework Core as default value for primary keys.
Thanks
You want a stored procedure, not a function:
create procedure [dbo].[GetLastId] (
#TableName nvarchar(max),
#LastId int output
) as
begin
declare #sql nvarchar(max);
set #sql = 'select #LastId = ISNULL(MAX(Id), 0) + 1 from ' + #TableName + ';'
EXECUTE sp_executesql #sql,
N'#LastId int output',
#LastId=#LastId output;
end;
You should also use quotename() around the table name to prevent unexpected things from happening.
Then you would call this as:
declare #lastId int;
exec dbo.GetLastId('Person', #lastid output);
update Person
set Id = #lastId;
You need to create stored procedure instead of function
create procedure [dbo].[GetLastId] (
#TableName nvarchar(max),
#ColumnName nvarchar(200),
#LastId int output
) as
begin
declare #sql nvarchar(max);
set #sql = 'select #LastId = ISNULL(MAX('+ #ColumnName +'), 0) + 1 from ' + #TableName + ';'
EXECUTE sp_executesql #sql,
N'#LastId int output',
#LastId=#LastId output;
end;
Then you can execute sp like below
declare #lastId int
exec dbo.GetLastId 'Person', 'Id' , #lastid output;
select #lastId
update Person
set Id = #lastId;

I want to use dynamic variable that is declared within dynamic SQL

declare #sql as nvarchar(500)=''
set #sql='
declare #N4 as int = 1
declare #ms as nvarchar(100) = concat(''ms'', convert(nvarchar(10), #N4))
select #ms
'
exec #sql
I want output as ms1.
DECLARE #SQL AS NVARCHAR(500)=''
SET #sql='
while (#i <10)
begin
PRINT (''MS_''+#I)
set #i=#i+1
end
'
EXEC(#SQL)
not generating value for #i
i want to put this code in while loop as I want to access ms1 to ms10
Use sp_executesql which supports ouput params
DECLARE #MS VARCHAR(50)
exec sp_executesql N'declare #N4 as int = 1;
SELECT #MS= concat(''ms'', convert(nvarchar(10), #N4))',
N'#MS VARCHAR(50) output', #MS output;
SELECT #MS
Yes, you can use and for that you need to use sp_executesql like this -
Declare #sql as nvarchar(500)='', #Params NVARCHAR(500),
#N4 Int = 1, #ms nvarchar(100)
SET #Params = '#N4 Int, #ms nvarchar(100) OUTPUT'
set #sql= N'SELECT #ms = concat(''ms'', convert(nvarchar(10), #N4))'
EXEC SP_EXECUTESQL #sql, #Params, #N4 = #N4, #ms = #ms OUTPUT
SELECT #ms
Use While statement and string concatenation to get your result :
DECLARE #StartValue INT = 1
DECLARE #EndValue INT = 10
DECLARE #Query VARCHAR(500) = ''
WHILE #StartValue < #EndValue
BEGIN
SET #Query = #Query + 'sms_' + CAST(#StartValue AS VARCHAR) + ','
SET #StartValue = #StartValue + 1
END
SELECT Query

How to swap in a parameter for a specific VARCHAR value being used for a linked server with openquery

When I execute this query(it's a scalar function) I get the result I want. It returns a shipping number.
select ShipmentNo from openquery([SERVER_IP], 'SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo = ''G015246''')
However, when I swap out G015246 with the parameter #PO:
select #ShipmentLabels = ShipmentNo from openquery([SERVER_IP], 'SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo = ''#PO''')
For some reason the return value becomes null with this change.
My return statement for both is:
RETURN ISNULL(#ShipmentLabels, #PO)
where #ShipmentLabels is the desired return value. Since it is null it returns #PO instead, which ends up being G015246 which seems right.
Anyone notice anything wrong with how I've added the #PO parameter to the sql statement?
Stored Procedure that calls the Function(fnGetTheShippingLabels):
I guess the best way to describe this SP is the starting point.
ALTER PROCEDURE [dbo].[StartingSP]
#JobId INT = 1
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ErrorMsg VARCHAR(256)
SELECT OC.PoNo,
V.FirstName,
V.LastName,
dbo.fnGetTradeAppFacilityName(NULL,OC.PoNo) TradeAppFacilityName,
OC.ReportFileName, dbo.fnGetTheShippingLabels(NULL,OC.PoNo) ShippingLabels
FROM PoHdr P
INNER JOIN OrderConfirmation OC ON
OC.PoNo = P.PoNo
INNER JOIN CpVendor V ON
V.VendNo = P.VendNo
WHERE OC.JobId = #JobId
SELECT #ErrorMsg ErrorMsg
END
As you can see from above, the function is in the SELECT statement of the SP.
Complete Scalar Function:
ALTER FUNCTION [dbo].[fnGetTheShippingLabels]
(
#PoHdrRecId INT,
#PoNo VARCHAR(20)
)
RETURNS VARCHAR(220)
AS
BEGIN
DECLARE #PO VARCHAR(20)= #PoNo
DECLARE #ShipmentLabels VARCHAR(220)
select #ShipmentLabels = ShipmentNo from openquery([SERVER_IP], 'SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo = ''G015246''')
--FROM dbo.PoHdr WHERE PoNo = #PoNo
-- Return the result of the functionRETURN ISNULL(#ShipmentLabels, #PO)
END
Trying a different way of writing query now, this doesn't work fyi:
Declare #TSQL VarChar(8000)
set #TSQL = N'select #ShipmentLabels = ShipmentNo from openquery([SERVER_IP],SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo =' + #PO +''
exec sp_executesql #TSQL, N'#ShipmentLabels output', #ShipmentLabels = #ShipmentLabels out
I see that it can be done in two ways
Option 1
Creating a dynamic query and Passing the output parameter
Declare #TSQL VarChar(8000)
DECLARE #SQ VARCHAR(4) = ''''
DECLARE #paramDef VARCHAR(100)
set #TSQL = N'select #ShipmentLabels = ShipmentNo from openquery([SERVER_IP],SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo =' + #SQ + #PO + #SQ + ')'
SET #paramDef = N'#ShipmentLabels VARCHAR(1000) OUTPUT'
exec sp_executesql #TSQL, #paramDef, #ShipmentLabels = #ShipmentLabels OUTPUT
Option 2 Creating a dynamic query, getting the value into a temp table and the read the value from temporary table.
Declare #TSQL VarChar(8000)
DECLARE #SQ VARCHAR(4) = ''''
DECLARE #ShipmentNo VARCHAR(1000)
set #TSQL = N'select ShipmentNo from openquery([SERVER_IP],SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo =' + #SQ + #PO + #SQ + ')'
CREATE TABLE #T
(
temp VARCHAR(max)
)
INSERT INTO #T
EXEC(#TSQL)
SELECT #ShipmentNo = temp from #t
SELECT #ShipmentNo
drop table #t
The complete solution with your stored procedure will look like, I have written it with a cursor and all, it can still be improved for performance, but with our limited on your DB design this is best solution we can provide for you.
ALTER PROCEDURE [dbo].[StartingSP]
#JobId INT = 1
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ErrorMsg VARCHAR(256)
DECLARE #PoNO VARCHAR(1000)
Declare #TSQL VarChar(8000)
DECLARE #SQ VARCHAR(4) = ''''
DECLARE #ShipmentNo VARCHAR(1000)
set #TSQL = N'select ShipmentNo,' + #SQ + #PO + #SQ +' as PoNo from openquery([SERVER_IP],SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo =' + #SQ + #PO + #SQ + ')'
CREATE TABLE #T1
(
temp VARCHAR(max)
)
INSERT INTO #T1
EXEC(#TSQL)
SELECT OC.PoNo,
V.FirstName,
V.LastName,
dbo.fnGetTradeAppFacilityName(NULL,OC.PoNo) TradeAppFacilityName,
OC.ReportFileName
,'' as
--, dbo.fnGetTheShippingLabels(NULL,OC.PoNo) ShippingLabels
INTO #T
FROM PoHdr P
INNER JOIN OrderConfirmation OC ON
OC.PoNo = P.PoNo
INNER JOIN CpVendor V ON
V.VendNo = P.VendNo
WHERE OC.JobId = #JobId
Declare #TSQL VarChar(8000)
DECLARE #SQ VARCHAR(4) = ''''
DECLARE #ShipmentNo VARCHAR(1000)
CREATE TABLE #T1
(
PoNo VARCHAR(1000),ShipmentNo VARCHAR(max)
)
DECLARE vend_cursor CURSOR
FOR SELECT PoNO FROM #T
OPEN vend_cursor
FETCH NEXT FROM vendor_cursor
INTO #PoNO
WHILE ##FETCH_STATUS = 0
BEGIN
set #TSQL = N'select ShipmentNo,' + #SQ + #PO + #SQ +' as PoNo from openquery([SERVER_IP],SET FMTONLY OFF EXEC GsvStaging.dbo.Paul_GetPoShippingLabels #CartId = NULL, #CpPoNo =' + #SQ + #PO + #SQ + ')'
INSERT INTO #T1
EXEC(#TSQL)
FETCH NEXT FROM vendor_cursor
INTO #PoNO
END
CLOSE vendor_cursor
DEALLOCATE vendor_cursor
UPDATE T
SET T.ShippingLabels = T1.ShipmentNo
FROM #T T
JOIN #T1 T1
ON T.PoNo = T1.PoNo
SELECT #ErrorMsg ErrorMsg
END

Can I use variable in condition statement as value with where condition that test that value

I have the following stored procedure:
ALTER proc [dbo].[insertperoll] #name nvarchar(50) , #snum int , #gnum int
as
DECLARE #value nvarchar(10)
SET #value = 's'+CONVERT(nvarchar(50),#snum)
DECLARE #sqlText nvarchar(1000);
DECLARE #sqlText2 nvarchar(1000);
DECLARE #sqlText3 nvarchar(1000);
declare #g nvarchar(50) = '''g1'''
SET #sqlText = N'SELECT ' + #value + N' FROM dbo.GrideBtable'
SET #sqlText2 = ' where Gnumber = '+#g --here is the problem it error invalid column name -- the #g is value from the table condition
set #sqlText3 = #sqlText+#sqlText2
Exec (#sqlText3) -- here how can i save the result of the exec into varibale
declare #sal nvarchar(50) = #sqlText3
insert employ (name,Snumber,Gnumber,Salary) values(#name,#snum,#gnum,#sal)
QUESTION: How to put in condition variable gets value from the table when i exec it it think that the #g is column but its not its a value from the table to test it so i display one value after the exec the other QUESTION is how to save the result from the exec in variable and then use that value
I'm using SQL Server 2008 (9.0 RTM)
This will be a stored procedure
Thanks in advance
Not sure why you would go through all the loops to insert into the table where you can have a simple insert query like ..
ALTER PROC dbo.[insertperoll] #name nvarchar(50) , #snum int , #gnum int
AS
insert employ (name, Snumber, Gnumber, Salary)
select #name
, #sum
, #gnum
, case when #snum = 1 then s1
when #snum = 2 then s2
when #snum = 3 then s3
when #snum = 4 then s4
end as Salary
from dbo.GrideBtable
where Gnumber = #gnum
If your intent is to have the proc retrieve a salary value from a column determined from the parameter snum and then make an insert into employ using the values passed as parameters and the salary retrieved I think you could refactor your procedure to this:
CREATE proc [dbo].[insertperoll] #name nvarchar(50) , #snum int , #gnum int AS
DECLARE #g NVARCHAR(50) = 'g1'
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'INSERT employ (name,Snumber,Gnumber,Salary) '
SET #sql += N'SELECT ' + QUOTENAME(#name, '''')
SET #sql += N', ' + CAST(#snum AS NVARCHAR(50))
SET #sql += N', ' + CAST(#gnum AS NVARCHAR(50))
SET #sql += N', s' + CAST(#snum AS NVARCHAR(50))
SET #sql += N' FROM dbo.GrideBtable'
SET #sql += N' WHERE Gnumber = ' + QUOTENAME(#g, '''')
EXEC (#sql)
Of course you could add the #g variable to the procedure parameters instead of having it hard coded in the procedure and call it as:
EXEC insertperoll #name='john', #snum=10, #gnum=100, #g='g1'
Sample SQL Fiddle (with some assumptions made about table structure)
You could do this using sp_executesql instead of exec() since this will allow you to use parameters, you can use an output parameter to get the value from the query:
DECLARE #SQL NVARCHAR(MAX) = N'SELECT #val = ' + CONVERT(NVARCHAR(10),#snum) +
N' FROM dbo.GrideBtable WHERE Gnumber = #G1';
DECLARE #val INT; -- NOT SURE OF DATATYPE REQUIRED
EXECUTE sp_executesql #SQL, N'#G1 VARCHAR(20), #val INT OUT', 'G1', #val OUT;

What is the best approach to filter data in SQL (Dynamic Query)?

I have a stored procedure which is supposed to return a result set from a table filtered according to parameters provided.
UPDATE
alter procedure Proc_CheckExchange
#Flag varchar(3),
#symbol varchar(13)=null,
#exchange char(3)=null,
#limit money=null,
#chargerate numeric(18,4)=null,
#ChgType char(2)=null,
#IsActive int=null,
#Mkrid varchar(11)=null,
#statecode varchar(4) =null
as
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if(#Flag='CHK')
begin
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N'and exchange=#exchange'+ cast(#exchange as nvarchar(100))
if len(isnull(#symbol, '')) > 0
set #sql = #sql + N'and symbol=#symbol'+ cast(#symbol as nvarchar(100))
if len(isnull(#limit, '')) > 0
set #sql = #sql + N'and limit=#limit'+ cast(#limit as nvarchar(100))
if len(isnull(#chargerate, '')) > 0
set #sql = #sql + N'and chargerate=#chargerate'+ cast(#chargerate as nvarchar(100))
if len(isnull(#ChgType, '')) > 0
set #sql = #sql + N'and ChgType=#ChgType'+ cast(#ChgType as nvarchar(100))
if len(isnull(#IsActive, '')) > 0
set #sql = #sql + N'and IsActive=#IsActive'+ cast(#IsActive as nvarchar(100))
if len(isnull(#statecode, '')) > 0
set #sql = #sql + N'and statecode=#statecode'+ cast(#statecode as nvarchar(100))
exec (#sql)
end
if (#Flag='ALL')
begin
select * from Tbl_StampDutyException1
end
UPDATE 1
alter procedure Proc_CheckExchange
#Flag varchar(3),
#symbol varchar(13)=null,
#exchange char(3)=null,
#limit money=null,
#chargerate numeric(18,4)=null,
#ChgType char(2)=null,
#IsActive int=null,
#Mkrid varchar(11)=null,
#statecode int =null
as
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if(#Flag='CHK')
begin
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = #exchange'
if len(isnull(#limit, '')) > 0
set #sql = #sql + N' and limit = #limit'
if len(isnull(#chargerate, '')) > 0
set #sql = #sql + N' and chargerate = #chargerate'
if len(isnull(#ChgType, '')) > 0
set #sql = #sql + N' and ChgType = #ChgType'
if len(isnull(#IsActive, '')) > 0
set #sql = #sql + N' and IsActive = #IsActive'
if len(isnull(#statecode, '')) > 0
set #sql = #sql + N' and statecode = #statecode'
if len(isnull(#symbol, '')) > 0
set #sql = #sql + N' and symbol = #symbol'
declare #params as nvarchar(max) = N'#Flag varchar(3),
#symbol varchar(13),
#exchange char(3),
#limit money,
#chargerate numeric(18,4),
#ChgType char(2),
#IsActive int,
#Mkrid varchar(11),
#statecode varchar(4)'
print #sql
--EXECUTE sp_executesql #sql, #params, #Flag, #symbol, #exchange, #limit, #chargerate, #ChgType, #IsActive, #Mkrid, #statecode
end
I am trying to create a stored procedure in which there will be as many conditions in WHERE clause as passed to the stored procedure. I hope I am clear about what I am trying to achieve. I am getting error Error converting data type varchar to numeric.
I think that a good solution would be this one
SELECT * from dbo.Clients
WHERE
(#param1 IS NULL OR field1 = #param1)
AND (#param2 IS NULL OR field2 = #param2)
With this approach the statement will not be re-processed (execution plan) by the server every time you execute the query.
What you could do is to rewrite your stored proc using dynamic SQL and include parts of where clause only if parameters are defined, e.g. replace the body with
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = ' + cast(#exchange as nvarchar(100))
-- add all parameters; you need to cast them to nvarchar if they have other type
exec (#sql)
As an improvement, you can use sp_executesql to execute dynamic SQL. See here on how to use it. In this case, the code will be:
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = #exchange'
-- add all parameters;
declare #params as nvarchar(max) = N'#Flag varchar(3),
#symbol varchar(13),
#exchange char(3),
#limit money,
#chargerate numeric(18,4),
#ChgType char(2),
#IsActive int,
#Mkrid varchar(11),
#statecode varchar(4)'
EXECUTE sp_executesql #sql, #params, #Flag, #symbol, #exchange, #limit, #chargerate, #ChgType, #IsActive, #Mkrid, #statecode
By the way, don't use select * in stored procedures, it's not a good practice. List all the columns you want to return. Otherwise, if the table definition changes, you will get different result to what it was previously.
What I guess you need is a CONDITIONAL WHERE CLAUSE approach, have provided the sample script below, you need to convert all the IF conditions to a CASE and you then you can combine all the conditions into a single SELECT statement:
SELECT * FROM Tbl_StampDutyException1
WHERE
exchange = CASE WHEN #exchange='' or #exchange is null THEN exchange ELSE #exchange END
AND symbol = Case when #symbol='' or #symbol is null THEN symbol ELSE symbol
AND *so on.....*