I have written a stored procedure Where I have written a query to get userid. There is a separate database for every userid. So I am trying to run a select query based on this userid obtained from my previous select query in a loop.
And I am trying to assign the columns in this select query to variables declared and use them further. But I am not understanding how to assign these to variables as I am getting errors
USE DATABASE1
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [User].[update_client_details]
AS
DECLARE
#clientdata CURSOR,
#clientid INT,
#SQL VARCHAR(2000),
#uid INT
#isactive INT,
#createdDate Date
BEGIN
SET #clientdata = CURSOR FOR
SELECT clientuserid FROM User.queen_client
OPEN #clientdata
FETCH NEXT
FROM #clientdata INTO #clientid
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = N'SELECT #uid=userid, #isactive=isactive, #createdDate=createddate FROM ['+CAST(#clientid AS NVARCHAR(20))+'].User.queen_user';
EXEC (#SQL)
IF(#isactive = 1)
BEGIN
//do someting//
END
END
CLOSE #clientdata
DEALLOCATE #clientdata
END
if the execute the store procedure it is getting executed and not stopping. If I force stop the execution then I am getting the error as "must declare the scalar variable "uid""
Query Which I tried
EXEC sys.sp_executesql N'SELECT #uid=userid, #isactive=isactive, #createdDate=createddate FROM ' +QUOTENAME(#clientid)+'.QueenBase.queen_user', N'#clienid int, #uid int OUTPUT, #createDate date OUTPUT';
Variables only persist and exist within the scope that they are declared in. Therefore both the following batches will fail:
DECLARE #I int = 1;
EXEC (N'SELECT #i;');
GO
EXEC (N'DECLARE #I int = 1;');
SELECT #i;
When using dynamic SQL, don't use EXEC(#SQL);, use sp_executesql. Then you can parametrise the statement. For example:
DECLARE #I int = 1;
EXEC sys.sp_executesql N'SELECT #i;', N'#i int', #i;
This returns 1. If you need to return a value to the outer SQL, as a parameter, you need to use OUTPUT parameters:
DECLARE #I int = 10;
DECLARE #O int;
EXEC sys.sp_executesql N'SELECT #O = #I / 2;', N'#I int, #O int OUTPUT', #I, #O OUTPUT;
SELECT #O;
This assigns the value 5 to the variable #O (which is then selected).
Also, don't use N'...[' + #SomeVariable + N'] ...' to inject dynamic values, it's not injection safe. Use QUOTENAME: N'...' + QUOTENAME(#SomeVariable) + N'...'
Additional note. The fact that you need to do something like N'FROM ['+CAST(#clientid AS NVARCHAR(20))+'].User.queen_user' suggests a severe design flaw, but that's a different topic.
If you do fancy additional reading, I cover a lot of considerations you need to take into account in my article Dos and Don'ts of Dynamic SQL.
For your attempt, it's not working as you use an expression for the first parameter (not a literal or variable) and then don't pass any of the parameters you define:
DECLARE #SQL nvarchar(MAX) = N'SELECT #uid=userid, #isactive=isactive, #createdDate=createddate FROM ' +QUOTENAME(#clientid)+'.QueenBase.queen_user;';
EXEC sys.sp_executesql #SQL, N'#isactive int OUTPUT, #uid int OUTPUT, #createDate date OUTPUT', #isactive OUTPUT, #uid OUTPUT, #createDate OUTPUT;
Related
It seems very simple solution, but I can't figure it out. Please help.
I have to call a stored proc with OUTPUT param from another stored proc. I think one of the issues is dynamic SQL, but I don't know how else to write it since #SQLWhere will change dynamically within C# code.
This is the proc being called from another proc:
ALTER PROCEDURE [dbo].[USP_RetrieveTotalRecord]
#SQLWhere AS NVARCHAR(1000),
#TotalRecordsFound as varchar(16) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL AS NVARCHAR(Max)
SET #SQL='Select #TotalRecordsFound = Count(Table_ID)
From TableName Where ' + #SQLWhere
EXEC(#SQL)
return
END
Here is how I am calling it from another proc:
Declare #TotalRec AS NVARCHAR(16);
Declare #SQLWhere AS NVARCHAR(1000);
SET #SQLWhere='Date Between ''12/13/2016'' AND ''12/14/2016'''
EXECUTE USP_RetrieveTotalRecord #SQLWhere, #TotalRec output;
Here is the error I am trying to resolve:
Msg 137, Level 15, State 1, Line 30
Must declare the scalar variable "#TotalRecordsFound".
Don't do what you are trying to do, Only pass values to stored procedure and then build the dynamic sql inside your procedure, something like ......
ALTER PROCEDURE [dbo].[USP_RetrieveTotalRecord]
#StartDate DATE = NULL
,#EndDate DATE = NULL
,#TotalRecordsFound INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(Max);
SET #SQL = N' Select #TotalRecordsFound = Count(Table_ID) '
+ N' From TableName Where 1 =1 '
+ CASE WHEN #StartDate IS NOT NULL THEN
N' AND [Date] >= #StartDate ' ELSE N' ' END
+ CASE WHEN #EndDate IS NOT NULL THEN
N' AND [Date] <= #EndDate ' ELSE N' ' END
EXEC sp_executesql #SQL
,N'#StartDate DATE, #EndDate DATE, #TotalRecordsFound INT OUTPUT'
,#StartDate
,#EndDate
,#TotalRecordsFound OUTPUT
END
Now #EndDate and #StartDate variables are optional , depending on what variable values you pass procedure will build the dynamic sql accordingly and return the results.
Also using parameterised query with sp_executesql will protect you against a possible SQL-Injection attach and also your proc will benefit from parameterised execution plans.
Not sure why you have a 2nd SP, just use one like so:
Declare #TotalRec AS NVARCHAR(16);
Declare #SQLWhere AS NVARCHAR(1000);
SET #SQLWhere='Date Between ''12/13/2016'' AND ''12/14/2016'''
SET #SQL='Select #TotalRecordsFound = Count(Table_ID)
From TableName Where ' + #SQLWhere
EXEC(#SQL)
Or use date variables if that's all you are using for selection (no dynamic sql necessary) - unless this is just a simplified example
--- comment section is broken, so, in response to get a value out, use something like this:
Ok - the simplest way is to use sp_ExecuteSQL
Declare #result int
Declare #sql nvarchar(max)
SET #SQL = ' SELECT COUNT(*) FROM MyTable'
exec sp_ExecuteSQL #sql, N' #Result int Output', #Result output
select #result as MyCount
M. ali, thanks for your help, but we have all SELECT, WHERE, and GROUP by criteria being passed from the application after dynamic selections. I needed a quick fix.
Finally I was able to resolve the issue using a temp table. I know they are not recommended, but I tried using Common table expression, table variable, but the #TotalRecordsFound was not visible outside the dynamic SQL. Therefore, created temp table, Inserted data into it using dynamic SQL, and then joined it with the next select statement.
I'm trying to append table name dynamically to SQL Server stored procedure, but I get an error:
couldn't find the stored procedure 's'
Code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[atest_demo_sp]
#name varchar(10)
AS
BEGIN
Declare #tname nvarchar
Declare #sql nvarchar
set #tname = #name
Set #sql = 'select * from ' + convert(varchar(10), #tname)
-- select #sql
execute sp_executesql #sql
END
You need to explicitly define a length for your string variables!
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[atest_demo_sp]
#name varchar(10)
AS
BEGIN
Declare #tname nvarchar(200) -- DEFINE A LENGTH HERE !!!
Declare #sql nvarchar(200) -- DEFINE A LENGTH HERE !!!
set #tname = #name
Set #sql = N'select * from ' + convert(nvarchar(10), #tname)
-- select #sql
execute sp_executesql #sql
END
Otherwise, your variables #tname and #sql will be exactly 1 character long! Probably not what you want!
See this blog post for more details: Bad habits to kick : declaring VARCHAR without (length)
Also, as a side note: either pick varchar (non-Unicode), or use nvarchar (Unicode) - but don't mix the two. And if you use nvarchar, you should always use the N'...' notation for assigning string literals.
The issue with the set #tname = #name, no need to assign the varchar to nvarchar here. Avoid the #tname variable and directly use #name in the SET statement since it is already varchar.
Hope the #name's first character only assigning to #tname. Also need to define the #sql to some length.
Use the below statement, it will work:
Declare #sql varchar(100);
Set #sql = 'select * from ' + #name
My Dynamic SQL queries have been placed inside a table. I want to read these from the table (in SQL server), do a parameter substitution, then execute the dynamic query.
i.e. Column GetFieldServerSQL contains the following:
SELECT top 1 Tenancy.LeadName
FROM Tenancy
RIGHT OUTER JOIN Property ON Tenancy.KeyProperty = Property.KeyProperty
WHERE (Tenancy.EndDate IS NULL) and Property.KeyProperty = #PropertyID
This is what I have tried:
declare #sql nvarchar(1000)
declare #sql2 nvarchar(1000)
declare #res nvarchar(1000)
declare #result nvarchar(1000)
set #sql = 'SELECT [GetFieldServerSQL]
FROM [SVSCentral].[dbo].[SVSSurvey_ExternalField] where ID=5'
exec #res = sys.sp_executesql #sql
print #res
This returns my query in the Results window, but I want it as a variable. #res only contains a 0 (for success)
Once I have it as a variable, I want to do a substitution. Something like:
set #sql2 = REPLACE(#result,'#propertyID','1003443')
(supposing #result is where my results is stored)
And then execute it:
exec (#sql2)
Instead of doing this:
exec #res = sys.sp_executesql #sql
You need to insert the results into a table, then select from that table, like this:
DECLARE #resTable TABLE (res nvarchar(1000))
INSERT INTO #resTable (res)
exec (#sql)
SELECT #res=res from #resTable
print #res
I'm trying to build a dynamic stored procedure to allow me to pass my string where clause as part of the parameter. However, following this article from MSDN I still couldn't get it running and having the error as described by the title. Any help is much appreciated.
EXEC [dbo].[Get_Demand_By_Click_Test]
#Year = 2014
Error:
Msg 8178, Level 16, State 1, Line 9
The parameterized query '(#Year int)
SELECT
dbo.vw_Demand_By.*
FROM '
expects the parameter '#Year', which was not supplied.
Stored procedure:
ALTER PROCEDURE [dbo].[Get_demand_by_click_test]
#Year INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET nocount ON;
-- Insert statements for procedure here
DECLARE #SQL NVARCHAR(max),
#ParamDefinition NVARCHAR(max)
SET #SQL = N'
SELECT
dbo.vw_Demand_By.*
FROM
dbo.vw_Demand_By
WHERE
Year(EventStartDate) = #Year'
SET #ParamDefinition = N'#Year int';
EXECUTE Sp_executesql
#SQL,
#ParamDefinition;
END
You have to pass the parameter as well:
EXECUTE Sp_executesql #SQL, #ParamDefinition, #Year = #Year;
You need to give a value to the #Year parameter. To execute the code use:
EXECUTE Sp_executesql
#SQL,
#ParamDefinition,
#Year = 2014
UPDATE
In response to your comments you can assign any variable in the place of the fixed 2014 value. In your code it will look like :
ALTER PROCEDURE [dbo].[Get_demand_by_click_test]
#Year INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET nocount ON;
-- Insert statements for procedure here
DECLARE #SQL NVARCHAR(max),
#ParamDefinition NVARCHAR(max)
SET #SQL = N'
SELECT
dbo.vw_Demand_By.*
FROM
dbo.vw_Demand_By
WHERE
Year(EventStartDate) = #Year'
SET #ParamDefinition = N'#Year int';
EXECUTE Sp_executesql
#SQL,
#ParamDefinition,
#Year = #Year
END
I'm trying to verify a job number exists on a linked server and get back a variable (#JobExists) indicating whether it does or not (1 for yes, 0 for no).
To do this I'm trying to use OPENQUERY along with sp_executesql as I have to pass in a parameter for the job number. I've tried the code below but get the error 'Procedure expects parameter '#statement' of type 'ntext/nchar/nvarchar'. For testing purposes I've declared and set the variable #JobNumber.
DECLARE #JobNumber as varchar(50)
SET #JobNumber = '2112111'
DECLARE #JobExists as BIT
DECLARE #JobCount as int
DECLARE #ParmDefinition as varchar(100)
DECLARE #sql as varchar(500)
SET #JobExists = 0
SET #ParmDefinition = N'#Result int output'
SET #sql = 'SELECT #Result = SELECT COUNT(*) FROM OPENQUERY(MYLINKEDSVR,''SELECT JOB_NUMBER FROM PROD.tbl1 WHERE JOB_NUMBER = ''''' + UPPER(RTRIM(LTRIM(#JobNumber))) + ''''''')'
exec sp_executesql #sql, #ParmDefinition, #Result = #JobCount output
IF #JobCount > 0
SET #JobExists = 1
SELECT #JobExists
I've read up sp_executesql here: http://technet.microsoft.com/en-us/library/ms188001.aspx
I've also done various searches but haven't come across any answers that work for me.
Is there something I'm missing?
The error message is clear: you must declare #sql as nvarchar and not as varchar.
The same for #ParamDefinition:
DECLARE #ParmDefinition as nvarchar(100)
DECLARE #sql as nvarchar(500)