SP_EXECUTESQL and Output Parameter - sql

I would like get the ID from the query, but I am getting a NULL, where is my mistake?
DECLARE #TblZimz NVARCHAR(256)
DECLARE #IdModul INTEGER
DECLARE #Id INTEGER
SET #TblZimz = '_ZIMZ000001'
SET #IdModul = 1
--SET #Id = -1
EXECUTE [InsertZimz] #TblZimz, #IdModul, #Id OUTPUT
ALTER PROCEDURE [InsertZimz]
#TblZimz NVARCHAR(256)
, #IdModul NVARCHAR(256)
, #Id INTEGER OUTPUT
DECLARE #SqlQuery NVARCHAR(MAX)
SET #SqlQuery = 'SELECT TOP (1) ([ID]) FROM ' + #TblZimz + ' WHERE [ModulId] = ' + #IdModul
EXEC SP_EXECUTESQL #SqlQuery, N'#Id INTEGER OUTPUT', #Id OUTPUT
why the #Id Paramter is alwasy null? I cant see my mistake?

First, select the desired id in an output variable using #Id = ([ID]) then assign this #Id OUTPUT value in the #Id variable using #Id = #Id OUTPUT. Also, you should pass data in where clause using a variable to avoid sql injection problem like [ModulId] = #IdModul (i.e. you should not concatenate it like [ModulId] = ' + #IdModul). try this :
DECLARE #SqlQuery NVARCHAR(MAX)
SET #SqlQuery = 'SELECT TOP (1) #Id = ([ID]) FROM '
+ #TblZimz + ' WHERE [ModulId] = #IdModul'
EXEC SP_EXECUTESQL
#SqlQuery,
N'#Id INT OUTPUT, #IdModul INT',
#IdModul = #IdModul,
#Id = #Id OUTPUT
Check details of SP_EXECUTESQL here

Like Deepak's answer, but easier:
EXEC SP_EXECUTESQL #SqlQuery,
N'#Id INT OUTPUT, #IdModul INT',
#IdModul OUTPUT, #Id

Here's an example of returning a dynamically defined proc's return value (rather than select results):
CREATE PROC p AS return 3
GO
DECLARE #proc varchar(30) = 'p', #retval int
DECLARE #qry nvarchar(max) = 'EXEC #i = ' + #proc --plus params as needed
EXEC sp_executesql #qry, N'#i INT OUTPUT', #retval OUTPUT
select #retval --returns 3
(Use case: to do a full "data run," I cursor through a list of procs I run, each of which returns a row count for logging purposes. Chased my tail quite a while before cracking this.)

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;

How to get value by dynamic field Name using sql select query

I am passing dynamic column name base that column name to get the value and below i my table
Table_CandidateInfo
Id Name Age City
1 Mazhar 30 Gulbarga
20 Khan 29 Bidar
Example1
Declare #ColumnName varchar(100), #Id int
set #ColumnName='Age'
set #Id=20
select * from Table_CandidateInfo where ID=#Id and
I am not able to pass ColumnName with and query because column name is dynamic pass by code. My output should be
29
Example2: If my #ColumnName='City' and #Id=20 then output should be like below
Bidar
I think what you are actually after is the below:
DECLARE #ColumnName sysname, #Id int;
SET #Id = 29;
SET #ColumnName = N'Age';
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT ' + QUOTENAME(#ColumnName) + N' FROM dbo.Table_CandidateInfo WHERE Id = #Id;';
--PRINT #SQL; --Your debugging friend
EXEC sp_executesql #SQL, N'#Id int', #Id = #Id;
Alas, you cannot pass identifiers as parameters. You need to use dynamic SQL:
declare #columnName varchar(100);
declare #Id int;
set #ColumnName = 'Age' ;
set #Id = 20;
declare #sql nvarchar(max);
set #sql = '
select *
from Table_CandidateInfo
where [columnName] = #Id';
select #sql = replace(#sql, '[columnName]', quotename(#columnName));
exec sp_executesql #sql,
N'#id int',
#id=#id;

Issue with using like with a wildcard in dynamic SQL statement

I am trying to use a dynamic SQL statement in a stored procedure to filter a query depending on the input from a user. I am starting basic with just one parameter that needs to find matching forenames in the database.
CREATE PROCEDURE dbo.uspFilter
#FirstName varchar(100) = null,
#Debug bit = 1
AS
DECLARE #SQL nvarchar(max);
DECLARE #Params nvarchar(max);
DECLARE #Search nvarchar(300);
SELECT #Params = N'#FirstName varchar(300) = null'
SELECT #SQL = N'SELECT * FROM Table WHERE 1=1'
IF #FirstName IS NOT NULL
SELECT #Search = N'#FirstName' + N'%'''
SELECT #SQL = #SQL + N' AND Forename LIKE ''' + #Search
IF #Debug = 1
#PRINT #SQL
EXEC sp_executeSQL #SQL, #Params, #FirstName = #FirstName;
GO
EXEC dbo.uspFilter #FirstName = 'Test', #Debug = 1;
GO
The output of the debug statement looks correct but it is not returning any results:
SELECT * FROM Table WHERE 1=1 AND Forename LIKE '#FirstName%'
EDIT: This is a meant to be a trimmed down version of what I am looking for - I will be using multiple parameters with different search criteria.
This is how you need to do it:
CREATE PROCEDURE dbo.uspFilter
#Search varchar(300) = null,
#Debug bit = 1
AS
DECLARE #SQL nvarchar(max);
DECLARE #Params nvarchar(max);
SELECT #Params = N'#FirstName varchar(300)';
SELECT #SQL = N'SELECT * FROM [Table]';
IF #FirstName IS NOT NULL BEGIN
SET #SQL = #SQL + N' WHERE Forename LIKE #FirstName + ''%'';';
END ELSE BEGIN
SET #SQL = #SQL + N';';
END
IF #Debug = 1
PRINT #SQL;
EXEC sp_executeSQL #SQL, #Params, #FirstName = #Search;
GO
EXEC dbo.uspFilter #Search = 'Steve', #Debug = 1;
Note that although Crowcoder's comment about SQL Injection is important, you can write dynamic SQL that doesn't allow for Injection. Such as the above.
Edit: Couple of slight corrections.
CREATE PROCEDURE dbo.uspFilter
#Search varchar(300) = null,
#Debug bit = 1
AS
DECLARE #SQL nvarchar(max);
DECLARE #Params nvarchar(max);
SELECT #Params = N'#FirstName varchar(300)';
SELECT #SQL = N'SELECT * FROM [YourTable]';
IF #Search IS NOT NULL BEGIN
SET #SQL = #SQL + N' WHERE Forename LIKE #FirstName;';
END ELSE BEGIN
SET #SQL = #SQL + N';';
END
IF #Debug = 1
PRINT #SQL;
EXEC sp_executeSQL #SQL, #Params, #FirstName = #Search;
GO
EXEC dbo.uspFilter #Search = 'Rameshbhai%', #Debug = 1;
go
pass parameter with wildcard values like 'searchtext%'
You can also apply dynamic where condition as below without make dynamic query, please have look at below sample example. You can join other filter by applying AND where clause with below example.
Declare #firstName VARCHAR(50)='sa'
SELECT
*
FROM Table
WHERE
(
(Forename LIKE '%'+#firstName+'%' AND #firstName!='')
OR
(#firstName='')
)

Stored Procedure giving error

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

Dynamic sql statement to update a variable

my sql statement is something like this below
DECLARE #OLD_NAV_VALUE AS INT
DECLARE #FINAL AS INT
SELECT #OLD_NAV_VALUE = [col1] from TBL_BA where DATE = #id_Date
SET #FINAL = #OLD_NAV_VALUE * 50
But the problem i am haveing here is that the column name in the select statement which is given as [col1] is a dynamic value. So i am trying something like this below.
DECLARE #OLD_NAV_VALUE AS INT
DECLARE #FINAL AS INT
EXEC('SELECT #OLD_NAV_VALUE = [' + #DYNAMIC_COL_NAME + '] from TBL_BA where DATE = ' + #id_Date)
SET #FINAL = #OLD_NAV_VALUE * 50
this gives an error that #OLD_NAV_VALUE has to be declared. So i tried declaring #OLD_NAV_VALUE inside the EXEC statement. But if i do this i am not able to use the same outside the EXEC statement.
Please let me know how to do this.
You can also use the sp_executesql statement with an output parameter:
declare #field nvarchar(50);
set #field = N'FieldToSelect';
declare #sql nvarchar(3000);
declare #parmDefinition nvarchar(500);
SET #parmDefinition = N'#returnValueOUT nvarchar(50) OUTPUT';
set #sql = N'SELECT #ReturnValueOUT = ' + #Field + ' FROM [TableName] WHERE [SomeCondition]'
declare #returnValue nvarchar(50);
EXECUTE sp_executesql #sql, #parmDefinition, #returnValueOut = #returnValue OUTPUT;
SELECT #returnValue
First, I'd suggest that you do a Google on "Erland dynamic SQL" and read his white paper on the subject.
Your design is probably not the best if it requires that you use a dynamic column name like this.
The reason that you can't do what you're trying to do is that everything in the EXEC is entirely in its own scope. If you absolutely have to do it this way though then you could use a table (either a normal table, or a global temporary table) to store the value for use outside of the EXEC.
We've used sp_executesql. Here's another example of a parameterized record count:
DECLARE #sql AS nvarchar(MAX)
SET #sql = N'SELECT #RecordCount = COUNT(*) FROM [{#SchemaName}].[{#TableName}]'
SET #sql = REPLACE(#sql, '{#SchemaName}', #SchemaName)
SET #sql = REPLACE(#sql, '{#TableName}', #TableName)
DECLARE #RecordCount AS int
EXEC sp_executesql
#query = #sql,
#params = N'#RecordCount INT OUTPUT',
#RecordCount = #RecordCount OUTPUT
This worked for me.
I declared a temp table and used it to receive the values from the select statement.
Something like below.
declare #i int
declare #v int
create table #t (val int)
insert into #t
exec ('declare #i int set #i = 0 select #i+1')
select * from #t