I have the following SQL:
DECLARE #SQL NVARCHAR(500)
DECLARE #Count INT
SET #SQL = 'SELECT #Count = COUNT(*) FROM customers WHERE lastname = ''alex'''
print #sql
EXECUTE sp_executesql #SQL, N'#Count int OUTPUT'
SELECT #Count
When I run it, I get following error. Any idea what I am doing wrong?
Msg 8178, Level 16, State 1, Line 1
The parameterized query '(#Count int OUTPUT)SELECT #Count = COUNT(*) FROM customers WHERE ' expects the parameter '#Count', which was not supplied.
You missed to supply #Count parameter as a part of sp_executesql call (note that in this case it should be marked OUTPUT as well):
...
EXECUTE sp_executesql #SQL, N'#Count int OUTPUT', #Count OUTPUT
SELECT #Count
The second pararmeter in the sp_executesql call is the query parameters definition, or so called formal parameters (in your case there is single parameter N'#Count int OUTPUT'). If query is parameterized, then what should go after (the third and subsequent parameters) are so called actual parameters.
Below samples may explain better:
-- sample query #1
-- will fail, because of query is pararmeterized,
-- and actual pararmeter is not provided
EXEC sp_executesql N'SELECT #Num', N'#Num int';
-- sample query #2
-- formal parameter is #Num of type int
-- actual parameter is literal constant 153
EXEC sp_executesql N'SELECT #Num', N'#Num int', 153;
-- sample query #3
-- formal parameter is #Num of type int
-- actual parameter is variable #p
DECLARE #p int;
SET #p = 1;
EXEC sp_executesql N'SELECT #Num', N'#Num int', #p;
-- sample query #4
DECLARE #p int;
SET #p = 15;
EXEC sp_executesql N'SELECT #Num=#Num*2', N'#Num int output', #p output
select #p;
Related
I want add values to multiple columns
ALTER PROCEDURE DynamicInsertQuery
#ColumnName VARCHAR(MAX),
#RiD VARCHAR(50)
AS
BEGIN
DECLARE #DynamicQuery NVARCHAR(MAX)
SET #DynamicQuery = 'UPDATE tbl_route_info SET ('+ #ColumnName +') = (1) WHERE RouteId=('+#RiD+')'
EXEC(#DynamicQuery)
END
This is the code I tried.
I run this procedure like this
DynamicInsertQuery '(1,2)','10'
I suspect you want something like this:
ALTER PROCEDURE DynamicInsertQuery (
#ColumnName VARCHAR(MAX),
#RiD VARCHAR(50)
) AS
BEGIN
DECLARE #DynamicQuery NVARCHAR(MAX);
SET #DynamicQuery = 'update tbl_route_into set #ColumnName = 1 where RouteId = #RiD';
SET #DynamicQuery = REPLACE(#DynamicQuery, '#ColumnName', #ColumnName);
EXEC sp_executesql #DynamicQuery, N'#RiD VARCHAR(50)', #RiD = #RiD;
END;
Notes:
You have too many parentheses in your version.
If you are learning to use dynamic SQL, then learn sp_executesql -- and how to use it to pass parameters.
You cannot pass names of things (columns, tables, etc.) as parameters, so that has to be placed directly in the string.
You can pass values into the string, such as #RiD.
Declare #Id bigint
EXEC procGetMaxNumber #Id output,'employee'
I'm getting an error message
Error converting data type varchar to bigint
when I execute the procedure with the above parameters
-- =============================================
-- Description: <Fetches Max Id for a particular table>
-- =============================================
CREATE PROCEDURE [dbo].[procGetMaxNumber]
(
#Id BIGINT OUTPUT,
#TblName nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
Declare #Query nvarchar(max) = ''
set #Query = 'SELECT ' + #Id + ' = isnull(Max(Id),0)+ 1 from ' + #TblName
--print #Query
Exec (#Query)
END
This looks extremely dangerous to me. What exactly are you going to do with the "next" Id value once you get it? You know that immediately after you retrieve this value, someone could insert a row and take it (or even fail to insert a row, and still take it even if the transaction rolled back), right? If you want to reserve an Id value and be sure that is the number you get, just run the insert, don't do any of this max+1 to insert later.
Anyway you can't use EXEC to retrieve an output parameter from dynamic SQL, you'll need to use sp_executesql:
DECLARE #query NVARCHAR(MAX) = N'SELECT #Id = COALESCE(MAX(Id),0)+1
FROM dbo.' + QUOTENAME(#TblName) + ';';
EXEC sp_executesql #query, N'#Id INT OUTPUT', #Id OUTPUT;
I have an issue executing below piece of code:
DECLARE #CUTOFFDAYS_i INT
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SQL1 NVARCHAR(MAX)
SET #CUTOFFDAYS_i = 750
CREATE TABLE #TMP(
EMPID INT,
EMPNAME VARCHAR(35)
)
SET #SQL = 'SELECT EMPID, EMPNAME INTO #TMP FROM EMPDB..EMPTABLE
WHERE DATEDIFF(DAY, CREATEDDATE, GETDATE()) > #CUTOFFDAYS_i
AND ERRORMESSAGE = '''''
SET #SQL1 = 'SELECT * FROM #TMP'
EXEC SP_EXECUTESQL #SQL
EXEC SP_EXECUTESQL #SQL1
Even then I don't get any result upon
SELECT * FROM #TMP
Your usage of # is wrong. #TMP is a temporary table that's only availble to the connection that makes it. I think what you're looking for is ##TMP.
But if you need such a temporary table, I would advice you to make a proper table instead as it's much easier to manage and keep track off and keeps less strain on your tempdb.
First of all you should declare #SQL and #SQL1 as a fixed maximum length varchar not exceeded 8000 (I guess it's not the restriction for the current MS SQL version???):
DECLARE #SQL NVARCHAR(1000)
DECLARE #SQL1 NVARCHAR(1000)
Then you shouldn't use () when call SP_EXECUTESQL
EXEC SP_EXECUTESQL #SQL
EXEC SP_EXECUTESQL #SQL1
And the finally you should use global temp table ##TMP in SP_EXECUTESQL for INSERT and then SELECT or you should declare local #Tmp table before the first SP_EXECUTESQL call
DECLARE #CUTOFFDAYS_i INT
DECLARE #SQL NVARCHAR(1000)
DECLARE #SQL1 NVARCHAR(1000)
SET #CUTOFFDAYS_i = 750
SET #SQL = 'SELECT EMPID, EMPNAME INTO ##TMP FROM EMPDB..EMPTABLE
WHERE DATEDIFF(DAY, CREATEDDATE, GETDATE()) > #CUTOFFDAYS_i
AND ERRORMESSAGE = '''''
SET #SQL1 = 'SELECT * FROM ##TMP'
EXEC SP_EXECUTESQL #SQL
EXEC SP_EXECUTESQL #SQL1
Consider the following stored procedure :
create procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
exec sp_executesql #p_SqlStatement
if ##ROWCOUNT = 1
begin
select 1;
end
else if ##ROWCOUNT <> 1
begin
select 0;
end
end
This stored procedure currently returns 2 datasets, one with the exec sp_executesql #p_SqlStatement data, and the other one would be either 1 or 0. Is there a way to suppress the first dataset? I mean, would it be possible that this stored procedure returns only 1 or 0 ?
I tried adding a RAISERROR( 'MyError', 18, 1 ) right after the exec sp_executesql #p_SqlStatement and then in the catch block select something else, but the first result set is always returned to my stored procedure caller...
You can embed the query in an if exists(.
alter procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
set #p_SqlStatement = 'if exists('+#p_SqlStatement+') select 1 else select 0'
exec sp_executesql #p_SqlStatement
end
There are however some queries where this won't fly.
Multiple statements
Queries terminated with ;
Queries that uses CTE's
There might be more but these are the ones I can think of right now.
Update:
You could try to use openrowset.
alter procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
declare #S nvarchar(max)
set #S =
'if exists(
select *
from openrowset(
''SQLNCLI'',
''Server=localhost;Trusted_Connection=yes;'',
'+quotename(#p_SqlStatement, '''')+'
) as T
)
select 1
else
select 0'
exec (#S)
end
I have never used this in productions but from the tests I made i looks like it should work with SP's, CTE's and multiple lines.
You have to allow ad hoc distributed queries.
You may try the NOCOUNT statement ( http://msdn.microsoft.com/en-us/library/ms189837.aspx )
create procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
SET NOCOUNT ON;
exec sp_executesql #p_SqlStatement
if ##ROWCOUNT = 1
begin
select 1;
end
else if ##ROWCOUNT <> 1
begin
select 0;
end
SET NOCOUNT OFF;
end
try this approach:
declare #mycount bigint
exec sp_executesql N'select #mycount = count(name) from Page where name like ''P%''', N'#mycount bigint OUTPUT', #mycount OUTPUT
select #mycount
It is important that your statement #p_SqlStatement incorporates a count.
If that is not the case, meaning your want to run this sp for any SQL you come upon, then this is no help. I don't think your can't suppress output of the sp_executesql.
Edit: You could also try this:
declare #mycount bigint
exec sp_executesql N'SELECT * INTO ##MyTempTable from Page where name like ''P%'''
select count(*) from ##MyTempTable
drop table ##MyTempTable
This means that you will have to add the following to every query (don't know if this works with sp's?) "SELECT * INTO ##MyTempTable FROM " - That shouldn't be to hard.
"##temptables" are global scoped temptables. Which means that they are also available outside the sp_executesql sp. You must drop the table explicitly.
I was not able to find any other workaround than using the suggested OPENROWSET. However, I found a way to be independant from the servername / instance. I still have to reconfigure the server to accept ad hoc distributed queries. Here is the final result :
create procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
declare #sql nvarchar(max) = N'SELECT * INTO ##TMP FROM OPENROWSET(''SQLOLEDB'',''Server=' + ##SERVERNAME + ';Trusted_Connection=Yes;'',''' + #p_SqlStatement + ''')';
exec sp_executesql #sql
if ( select COUNT(1) from ##TMP ) = 1
begin
select 1;
end
else
begin
select 0;
end
drop table ##TMP;
end
This solution has it's limitations :
Every column in the #p_SqlStatement must have a name
I have to enable Ad Hoc Distributed Queries on my server.
I have to use #sql variable along with sp_executesql on the OPENROWSET because I was not able to use variables inside OPENROWSET in another way, so this makes dynamic SQL on an OPENROWSET, which is pretty bad performance-wise.
I do reconfigure the server using the following script :
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
GO
I wanna know how to get field value from dynamic query. I do it on stored-procedure in MySQL. I've the following code:
...
DECLARE l_query VARCHAR(500);
DECLARE l_table VARCHAR(50);
SET l_table = 'tb_user';
SET #l_query = concat('SELECT count(1) FROM ', l_table);
-- #Note that l_table will not always for tb_user,
-- it can be changed with other table name.
PREPARE l_sql FROM #l_query;
EXECUTE l_sql;
...
The question is, how to get value for count result (count(1)) ..? I need this value, because it will be used on the next process at the same stored procedure.
Many thanks before.
Short: Use SELECT INTO to select the value into variable.
In your case:
...
DECLARE l_query VARCHAR(500);
DECLARE l_table VARCHAR(50);
DECLARE cnt INTEGER;
SET l_table = 'tb_user';
SET #l_query = concat('SELECT count(1) INTO #cnt FROM ', l_table);
-- #Note that l_table will not always for tb_user,
-- it can be changed with other table name.
PREPARE l_sql FROM #l_query;
EXECUTE l_sql;
-- Use cnt variable here
...