I'm executing a dynamic query from a variable but it always says:
Could not find stored procedure
I have tried to use simpler queries like set #query = 'select * from [table_name]' but it gives the same error.
CREATE TABLE #test
(
id INT IDENTITY(1, 1) NOT NULL,
name VARCHAR(200)
)
DECLARE #TRIGER_NAME VARCHAR(200)
DECLARE #V_MAX INT
DECLARE #V_MIN INT
DECLARE #QUERY VARCHAR(MAX)
SELECT #V_MAX = MAX(id)
FROM #test
SELECT #V_MIN=Min(id)
FROM #test
WHILE #V_MIN <= #V_MAX
BEGIN
SELECT #TRIGER_NAME = name
FROM #test
WHERE id = #V_MIN
SET #QUERY = 'DROP TRIGGER '+ #TRIGER_NAME;
EXEC #query
SET #V_MIN = #V_MIN+1
END
In my temporary table is the list of all my trigger name so the expected output I want to get is Command(s) completed successfully. Not 'Could not find stored procedure', because I supposed to executing a dynamic queries not a stored procedure.
Instead EXEC #query use, EXEC sp_executesql #query. From the docs:
Executes a Transact-SQL statement or batch that can be reused many
times, or one that has been built dynamically. The Transact-SQL
statement or batch can contain embedded parameters.
Or if you want to use EXEC, change it to:
EXEC (#query)
From the documentation you have:
Execute a character string { EXEC | EXECUTE }
( { #string_variable | [ N ]'tsql_string' } [ + ...n ] )
[ AS { LOGIN | USER } = ' name ' ] [;]
which means your #string_variable or string must be wrapped in ().
Related
I have a stored proc - [dbo].[DynamicPivotTableInSql1] that I need to call from another stored proc and continue further query creation. I see that the stored proc [dbo].[DynamicPivotTableInSql1] is executing successfully but the results are not getting returned. Could you please let me know if I am doing something wrong here.
I have very recently posted a related question, but on further analysis found that the results not getting returned to the calling stored proc is the issue.
I need to use dynamic sql in [dbo].[DynamicPivotTableInSql1], because the query is quite complicated and I have to extend the where clause based on certain input parameters. I have only posted a part of it.
Thanks for your help and time!
CREATE PROCEDURE [dbo].[DynamicPivotTableInSql1]
AS
BEGIN
DECLARE #colsPivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#str_comp1 AS NVARCHAR(MAX),
#str_comp2 AS NVARCHAR(MAX)
SET #str_comp1 = ' comp1 IN (''Capacity'') and '
SET #str_comp2 = ' comp2 IN (''Refinery'') '
SET #colsPivot = 'SELECT STUFF((SELECT '',''
+ quotename(link_id)
from [dbo].[test_excel_poc_head] t
WHERE ' + #str_comp1 + #str_comp2 + '
FOR XML PATH(''''), TYPE ).value(''.'', ''NVARCHAR(MAX)'')
,1,1,'''')'
EXEC(#colsPivot)
END
The calling procedure is below:
BEGIN
DECLARE #colsPivot1 AS NVARCHAR(MAX),
#return_result AS NVARCHAR(MAX)
EXEC #return_result = [dbo].[DynamicPivotTableInSql1]
print('Return Value: ' + #return_result)
END
Below is the result of executing stored proc [dbo].[DynamicPivotTableInSql1]:
But below is the returned result:
Your problem is that you're attempting to use a stored procedure as a function. A stored procedure returns a numeric value, 0 for success, or another value that represents an error.
Functions do not allow for calling things like EXEC, however, you can define a stored procedure's variable as OUT or OUTPUT (depending on the need) and in your case, it appears you only need OUT.
Here is an example of how you can do this:
/* Create base procedure with an OUT parameter */
CREATE OR ALTER PROCEDURE dbo.Procedure1 (
#results xml OUT -- OUT parameter to return the results.
)
AS
BEGIN
/* Build dynamic SQL statement */
DECLARE #statement nvarchar(1000) = 'SET #dynamicResults = ( SELECT * FROM Misc FOR XML PATH( '''' ), TYPE );';
/* Execute the dynamic SQL statement and capture results into the #results OUT parameter */
EXEC sp_executesql #statement, N'#dynamicResults xml OUT', #dynamicResults = #results OUT;
END
GO
/* Create a procedure that captures the results from the base procedure */
CREATE OR ALTER PROCEDURE dbo.Procedure2
AS
BEGIN
/* Declare variable to capture the results from Procedure1 */
DECLARE #results xml;
/* Call Procedure1 and capture its results */
EXEC dbo.Procedure1 #results OUT;
/* Return the results from Procedure1 */
SELECT #results AS ResultsFromProcedure1;
END
GO
/* Call Procedure2 */
EXEC dbo.Procedure2;
Calling Procedure2 in this example simply returns the results captured from Procedure1. Once you have the results from the first procedure, you can continue working with them.
I have created the following Select statement which is working fine:
SELECT #Block = [Blok], #Year = [Aar]
FROM [PT99991_Result].[dbo].[Testheader]
WHERE N = #TestHeaderID
The problem is that this Select statement is used in a While loop where the database can change to another one during the loop. I have tried to modify the statement to the following but it's not working. I have also tried to use EXEC which takes care of my problem but then I'm facing a problem with the local variables #Block and #Year instead.
SET #DataBase = 'PT99991_RESULT' --This is only for test!
SELECT #Block = [Blok], #Year = [Aar]
FROM '[' + #DataBase + '].[dbo].[Testheader]'
WHERE N = #TestHeaderID
I'm not sure what I'm doing wrong?
First, generate a T-SQL template like this:
DECLARE #DynamicTSQLStatement NVARCHAR(MAX) = N'SELECT #Block = [Blok], #Year = [Aar]
FROM [#DataBase].[dbo].[Testheader]
WHERE N = #TestHeaderID';
Then, let's say that the variale #Datbase holds your current database name. If it is not extracted from sys.database you can perform additional validation to ensure nobody is doing something wrong.
IF NOT EXISTS(SELEFT 1 FROM sys.database WHERE [name] = #Datbase
BEGIN
....
END;
After the validation, you just replace the database name in the template:
SET #DynamicTSQLStatement = REPLACE(#DynamicTSQLStatement, '#Database', #Database);
Then, execute the code passing the parameters:
EXEC sp_executesql #DynamicTSQLStatement
,N'#TestHeaderID INT'
,N'#Block INT OUTPUT'
,N'#Year INT OUTPUT'
,#TestHeaderID
,#Block OUTPUT
,#Year OUTPUT;
of course on every loop iteration, reset the template.
Instead of while loop, You can go for undocumented stored procedure: ms_foreachdb and execute against the databases and finally apply filter for the specific database.
Caveat: Don't use this in production code, because, it uses undocumented stored procedure.
CREATE TABLE #test(dbname sysname, blok int, aar int)
DECLARE #db_list NVARCHAR(max) = 'DB1,DB2'
EXECUTE master.sys.sp_MSforeachdb 'USE [?]; declare #blok int, #aar int; INSERT INTO #test SELECT db_name(), blok, aar from [dbo].[Testheader] WHERE N = TestHeaderId;'
SELECT * FROM #test where dbname in
(
SELECT value FROM string_split(#db_list,',')
)
Is there any way to get all parameter values from a stored procedure dynamically?
In other words, iterate through all parameters in one stored procedure to get their values into one string. This is for a unified logging process for a bunch of stored procedures.
I can get the names of parameters:
SELECT PARAMETER_NAME
FROM INFORMATION_SCHEMA.PARAMETER
WHERE SPECIFIC_NAME = 'procedure_name';
Also, I tried to use dynamic SQL commands. I've generated a command with included parameter, but EXEC can't execute command.
#cmd = 'SELECT '#UserID' + CONVERT(NVARCHAR(MAX), #UserID)
+ '#Date' + CONVERT(NVARCHAR(MAX), #Date)'
EXEC #cmd
Is there any way to do this besides manually generating a list of parameter values for each stored procedure?
Since SQL Server 2014 there is sys.dm_exec_input_buffer a table valued function with an output column event_info that gives the full execution statement (including parameters).
I use this for error logging in stored procedures.
For example:
--include this inside the stored procedure
declare #statement nvarchar(max)
select #statement = event_info
from sys.dm_exec_input_buffer(##spid, current_request_id())
--this will print whatever you called the procedure with (including parameters)
print #statement
-- if you want to parse just the parameters from the statement, it can be done like this
declare #proc_name varchar(128) = object_name(##procid)
declare #param_idx int = charindex(#proc_name, #statement) + len(#proc_name)
declare #param_len int = len(#statement) - #param_idx
declare #params nvarchar(max) = right(#statement, #param_len)
select #params
Basically I have a procedure that is supposed to take queries from a table and run them. This works fine except when I try to define a variable in the procedure that is referenced in the query. I simply define the variable using:
DECLARE #spcode as varchar(255)
SET #spcode = 'C'
And then in the table I reference it here:
...
where sp_flag = #spcode
...
My procedure then runs through the table and executes all of the queries in the table, this works if I simply set sp_flag = 'C', but if I try to set it to the variable defined in the procedure I get the following error:
Msg 137, Level 15, State 2, Line 7
Must declare the scalar variable "#spcode".
I have searched around but I have not been able to find a solution to this problem, perhaps someone has an idea of how I would go about fixing this problem?
Thanks,
Sam
Look at this example.
First snippet execute SQL statement without using variable. Second with.
create table tbl1 (id int identity(1,1), value varchar(100));
insert into tbl1 (value) values ('abc'), ('def'), ('xyz');
-- first snippet (without using variable)
declare
#sql varchar(max);
set #sql = 'select * from tbl1 where value = ''abc''';
exec(#sql);
-- second snippet (with variable)
declare
#sql nvarchar(max),
#param nvarchar(100);
set #param = N'#val varchar(100)'
set #sql = N'select * from tbl1 where value = #val';
exec sp_executesql #sql, #param, #val = 'abc';
Not sure how to implement this, but I need a way to get the current list of parameters for a stored procedure as well as their passed in values (this code will be executed in the stored procedure itself).
I know I can use sys.parameters to get the parameter names, but how to get the actual values?
What I need to do with this is to make a char string of the form
#param_name1=#value1,#param_name2=#value2,...,#param_namen=#valuen
I have tried to use dynamic sql, but not having much joy with that.
Any ideas??
Edit:
Currently I am just going through all the parameters one-by-one to build the string. However I want a "better" way to do it, since there are quite a few parameters. And incase parameters are added later on (but the code to generate the string is not updated).
I tried using dynamic sql but gave up, since the sp_executesql sp requires parameters be passed into it...
You state '(this code will be executed in the stored procedure itself).' so assuming you are in the procedure you will already know the parameter names as you have to declare them when creating your procedure. Just do a select and put the names inside text fields
ALTER PROCEDURE procname
(
#param1 NVARCHAR(255)
,#param2 INT
...
)
SELECT [Parameters] = '#param1=' + #param1
+ ',#param2=' + CONVERT(NVARCHAR(MAX),#param2)...
The CONVERT is there as an example for non-char datatypes.
update
You will need to create a linked server that points to itself to use the OPENQUERY function.
USE [master]
GO
/****** Object: LinkedServer [.] Script Date: 04/03/2013 16:22:13 ******/
EXEC master.dbo.sp_addlinkedserver #server = N'.', #srvproduct=N'', #provider=N'SQLNCLI', #datasrc=N'.', #provstr=N'Integrated Security=SSPI'
/* For security reasons the linked server remote logins password is changed with ######## */
EXEC master.dbo.sp_addlinkedsrvlogin #rmtsrvname=N'.',#useself=N'True',#locallogin=NULL,#rmtuser=NULL,#rmtpassword=NULL
GO
Now you can do something like this cursor to get each parameter name and then use dynamic sql in OPENQUERY to get the value:
DECLARE curParms CURSOR FOR
SELECT
name
FROM sys.parameters
WHERE OBJECT_ID = OBJECT_ID('schema.procedurename')
ORDER BY parameter_id
OPEN curParms
FETCH curParms INTO #parmName
WHILE ##FETCH_STATUS <> -1
BEGIN
SELECT #parmName + '=' + (SELECT * FROM OPENQUERY('linkedservername','SELECT ' + #parmName))
FETCH curParms INTO #parmName
END
CLOSE curParms
DEALLOCATE curParms
Since SQL Server 2014 we have sys.dm_exec_input_buffer, it is a table valued function with an output column event_info that gives the full execution statement (including parameters).
We can parse the param values from sys.dm_exec_input_buffer and get the param names from sys.parameters and join them together to get the string you want.
For example:
create procedure [dbo].[get_proc_params_demo]
(
#number1 int,
#string1 varchar(50),
#calendar datetime,
#number2 int,
#string2 nvarchar(max)
)
as
begin
-- get the full execution statement
declare #statement nvarchar(max)
select #statement = event_info
from sys.dm_exec_input_buffer(##spid, current_request_id())
-- parse param values from the statement
declare #proc_name varchar(128) = object_name(##procid)
declare #param_idx int = charindex(#proc_name, #statement) + len(#proc_name)
declare #param_len int = len(#statement) - #param_idx
declare #params nvarchar(max) = right(#statement, #param_len)
-- create param values table
select value, row_number() over (order by current_timestamp) seq
into #params
from string_split(#params, ',')
-- get final string
declare #final nvarchar(max)
select #final = isnull(#final + ',','') + p1.name + '=' + ltrim(p2.value)
from sys.parameters p1
left join #params p2 on p2.seq = parameter_id
where object_id = ##procid
select #final params
end
To test it:
exec get_proc_params_demo 42, 'is the answer', '2019-06-19', 123456789, 'another string'
Returns the string you want:
#number1=42,#string1='is the answer',#calendar='2019-06-19',#number2=123456789,#string2='another string'
I have something similar wrapped as a UDF. I use it for error logging in catch blocks.