I need to pass a table var to sp_executesql statement. Do you know how can I pass the table variable to sp_executesql?
Here is how I pass the regular variable (not table variable) to sp_executesql
EXEC sp_executesql #statement, N'#Status INT',#Status
Typically you don't pass a table variable to execute SQL with sp_executesql. You make a statement up out of text and execute that. Like so:
IF OBJECT_ID('tempdb..#People') IS NOT NULL
DROP TABLE tempdb..#People
CREATE TABLE #People (PersonId INT IDENTITY, PersonName VARCHAR(128));
INSERT INTO #People (PersonName) VALUES ('Brett'), ('John'), ('Mark'), ('Shawn'), ('Ryan'), ('Kevin');
DECLARE #SQL NVARCHAR(Max) = 'Select * from #People'
EXEC sp_executesql #Sql
UPDATE 1-27-17
IF OBJECT_ID('tempdb..#People') IS NOT NULL DROP TABLE tempdb..#People
IF OBJECT_ID('tempdb..#People2') IS NOT NULL DROP TABLE tempdb..#People2
CREATE TABLE #People (PersonId INT IDENTITY, PersonName VARCHAR(128));
CREATE TABLE #People2 (PersonId INT IDENTITY(7,1), PersonName VARCHAR(128));
SET NOCOUNT ON;
INSERT INTO #People (PersonName) VALUES ('Brett'), ('John'), ('Mark'), ('Shawn'), ('Ryan'), ('Kevin');
INSERT INTO #People2 (PersonName) VALUES ('Emily'), ('Beth'), ('Jane'), ('Hannah');
--I. getting an output for a single output variable dynamically
--Say I just want to get Ryan by his Id dynamically and output it
--I need to define one or many parameters OUTSIDE the scope of the Dynamic Sql
DECLARE #Output VARCHAR(8)
DECLARE #PersonId INT = 5
--I then need to associate the parameters as an array, for the purposes of explanation I will use DIFFERENT NAMES you may use the same
DECLARE #ParmDefinition NVARCHAR(500) = N'#PersonIdInside Int, #OutputInside varchar(8) OUTPUT'
--I then use the names ABOVE in the dynamic sql
DECLARE #SQL NVARCHAR(Max) = N'Select #OutputInside = PersonName from #People Where PersonId = #PersonIdInside'
-- I then do the following AFTER the sp_executesql 1. The Dynamic sql nvarchar, 2. The params nvarchar 3. one or many variables and how they associate
EXEC sp_executesql #Sql, #ParmDefinition, #PersonIdInside = #PersonId, #OutputInside = #Output OUTPUT
-- I have an output so now it should show what I want
SELECT #Output
-- II. getting a result set dymamically to another record set or table OUTSIDE the scope of the internal
-- Create another table, I use a #table for example purposes
IF OBJECT_ID('tempdb..#Output') IS NOT NULL DROP TABLE tempdb..#Output
CREATE TABLE #Output (PersonId INT IDENTITY, PersonName VARCHAR(8))
--Get a truncated list for an 'in' statement later of person Id's in a variable
DECLARE #People NVarchar(32) = N'1, 5, 10'
--I then use the #People ABOVE in the dynamic sql putting it together and then do an 'insert statement first'
DECLARE #SQL2 NVARCHAR(Max) = N'Insert Into #Output SELECT PersonName FROM (SELECT * FROM #People UNION SELECT * FROM #People2) as x Where PersonId in (' + #People + ')'
--execute yields nothing
EXEC sp_executesql #Sql2
-- or does it?
Select *
From #Output
-- !!! WARNING !!!
-- With dynamic sql you cannot nest multiple dynamic sql statements inside of procs. EG: Proc1 cannot call Proc2 and both of them have dynamic sql in them. Engine limitation.
Related
As described in the title, I'm looking to store the output of a variable into a "non-variable table". Here is the code:
declare #fullpath NVARCHAR(200) = '''Data Source=path\to\folder\'
declare #filename NVARCHAR(50) = 'Country.xlsx'
declare #properties NVARCHAR(50) = ';Extended Properties=Excel 12.0'')'
declare #sheetname NVARCHAR(50) = '...[Country$]'
declare #extraction NVARCHAR(500) =
'SELECT * FROM OPENDATASOURCE(''Microsoft.ACE.OLEDB.12.0'','+ #fullpath+
#filename+
#properties+
#sheetname
EXEC(#extraction)
This will output the content of the excel file in table format (country_id and country_name). Now, how would one store this output table in another table? For instance:
SELECT * INTO #temp FROM #extraction
Regards,
Just use INSERT INTO as you normally would. INSERT INTO allows for it to be followed by an EXEC statement:
CREATE TABLE #Temp ({Column List});
INSERT INTO #Temp ({Column List})
EXEC sys.sp_executsql #extraction;
Seems like the OP doesn't know the definition of the files. This, in truth, is a problem, but you can "get around" it. I would therefore SELECT ... INTO inside the dynamic statement. You'll need to use a global temporary table, or a user table for this though. I use a global temporary table for this example:
DECLARE #extraction nvarchar(MAX) =
N'SELECT * INTO ##TEMP FROM OPENDATASOURCE(''Microsoft.ACE.OLEDB.12.0'',' + #fullpath + #filename + #properties + #sheetname;
EXEC sys.sp_executesql #SQL;
SELECT *
FROM ##TEMP;
--DROP TABLE ##TEMP;
I'm having trouble sending a table variable into a sp_executesql parameter list.
my Table Variable:
declare #MemberCoverageIds table (CoverageId ID_t)
insert into #MemberCoverageIds( CoverageId) select MemberCoverageId from MemberCoverages where MemberNumber = #FulfillmentEntityIdentifier
My where clause using the table variable:
#WhereClause = #WhereClause + 'and F.FulfillmentEntityId in (select CoverageId from #MemberCoverageIds) '
it is part of my FinalSQL variable which has the rest of the statement:
select #FinalSQL = #InsertClause + #SelectClause + #JoinClause + #WhereClause
and then I have the execute:
exec sp_executesql #FinalSQL,
N' #FulfillmentEntityIdentifier RefNumber_t,
#MemberCoverageIds ReadOnly,
#EntityId Id_t,
#FulfillmentEntityType Code_t,
#FulfillmentDocumentType Code_t,
#FulfillmentMethod Code_t',
#FulfillmentEntityIdentifier = #FulfillmentEntityIdentifier,
#MemberCoverageIds = #MemberCoverageIds,
#EntityId = #EntityId,
#FulfillmentEntityType = #FulfillmentEntityType,
#FulfillmentDocumentType = #FulfillmentDocumentType,
#FulfillmentMethod = #FulfillmentMethod
I then get an unexpected error from that execution. I know it is the #MemberCoverages table variable because it worked before I added it. My question is what would be the proper syntax for sending the table variable in the parameter list? Do I have to declare it in the Parameter list?
You need to create a named type that your table variable will use:
create type dbo.myTableType as table (id int)
Then you can use it as a typed argument to sp_executesql:
declare #m dbo.myTableType;
insert #m values (1), (2)
exec sp_executesql
N'select 99 where 1 in (select id from #m)',
N'#m dbo.myTableType readonly',
#m
If you don't want to create a new type, you can use a #temp table:
declare #t table(id int);
insert #t values (1), (2);
select * into #t from #t;
exec sp_executesql N'select 99 where 1 in (select id from #t)';
I've different different tables to categorically store data and a log table where all the transactions log are recorded
e.g. 1) VoucherNO, Add, ...
2) VoucherNO, Delete, ..
After I backup the database and restore in another server for my Reporting Purpose. That time I want to ensure all the log data and transaction are available in TestDB if not then I remove log from 'AUD_USER_ACTIVITY'.
To find the transaction exist or not, I create a dynamic sql select statement and check whether record is exist or not.
Basis on #RecExist Value I do the action like if records is not available in TestDB the log will be remove, if record exist immediately break this loop and going for next procedure
But #RecExist variable is not updating in Dynamic SQL Execution. Please guide me
declare #MvDocNo varchar(50)
DECLARE #SCtr as DECIMAL(10,0)
declare #LocationCode varchar(4)
declare #UName Nvarchar(40)
declare #toe varchar(30)
declare #QryTxt as nvarchar(MAX);
Declare #RecExist as INT =0;
SET #RecExist=0
WHILE #RecExist=0
BEGIN
select top 1 #MvDocNo=DOCNO, #SCtr=SrlNo,#LocationCode =DMLTYPE,#UName=TABLENAME
FROM R_AUDDB..AUD_USER_ACTIVITY
WHERE DBNAME='TestDB' and DMLTYPE not in ('AD','D','PD') ORDER BY SRLNO DESC;
select top 1 #toe=docno from TestDB..M_TYPEOFENTRY where TBLNAME=#UName;
set #QryTxt='Select #RecExist=1 From R_TestDB..'+#UName+ ' Where '+#toe+'='''+#MvDocNo+''''
exec (#QryTxt)
IF #RecExist=0
BEGIN
DELETE R_AUDDB..AUD_USER_ACTIVITY WHERE SRLNO=#SCtr
END
END
The following code sample demonstrates how to check for a row in a table with a specific column and value using dynamic SQL. You ought to be able to change the values of the first three variables to reference a table and column in your database for testing.
Note that SQL injection is still possible: there is no validation of the table or column names.
-- Define the table to check and the target column name and value.
declare #TableName as SysName = 'Things';
declare #ColumnName as SysName = 'ThingName';
declare #TestValue as NVarChar(32) = 'Beth';
-- Create a SQL statement to check for a row in the target table with the specified column name and value.
declare #SQL as NVarChar(1024);
declare #Result as Bit;
-- Note that only object names are substituted into the statement at this point and QuoteName() is used to reduce problems.
set #SQL = N'select #iResult = case when exists ( select 42 from dbo.' + QuoteName( #TableName ) +
N' where ' + QuoteName( #ColumnName ) + N' = #iTestValue ) then 1 else 0 end;'
select #SQL as SQL;
-- Execute the SQL statement.
-- Note that parameters are used for all values, i.e. the target value and return value.
execute sp_executesql #stmt = #SQL,
#params = N'#iTestValue NVarChar(32), #iResult Bit output',
#iTestValue = #TestValue, #iResult = #Result output
-- Display the result.
select #Result as Result;
I am working on C# project which needs a stored procedure which will take two table names as inputs.
First table will copy data to a temp table which has two columns URL & channelID. This URL column is then matched with other input table's URL column & if match is found then it will update channel id from temp table to other tables channel ID.
I have written stored procedure as
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
END
While EXISTS(SELECT * From #Temp ) > 0
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channelid
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channelid
End
I don't have much knowledge in TSQL and my above code has errors.
Incorrect syntax near '>'.
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 20
Must declare the scalar variable "#channelid".
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 22
Must declare the scalar variable "#channelid".
Please suggest what changes needs to done
I don't have MS SQL server handy to test it, but you declare your variable as #channel_Id, and later try to use it as #channelid (without the underscore) so you get errors about the undeclared variable.
I've corrected your SP and this is how it should look
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
While EXISTS(SELECT * From #Temp )
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channel_Id
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channel_Id
End
END
I have a TSQL sproc that builds a query as and executes it as follows:
EXEC (#sqlTop + #sqlBody + #sqlBottom)
#sqlTop contains something like SELECT TOP(x) col1, col2, col3...
TOP(x) will limit the rows returned, so later I want to know what the actual number of rows in the table is that match the query.
I then replace #sqlTop with something like:
EXEC ('SELECT #ActualNumberOfResults = COUNT(*) ' + #sqlBody)
I can see why this is not working, and why a value not declared error occurs, but I think it adequately describes what I'm trying to accomplish.
Any ideas?
use sp_executesql and an output parameter
example
DECLARE #sqlBody VARCHAR(500),#TableCount INT, #SQL NVARCHAR(1000)
SELECT #sqlBody = 'from sysobjects'
SELECT #SQL = N'SELECT #TableCount = COUNT(*) ' + #sqlBody
EXEC sp_executesql #SQL, N'#TableCount INT OUTPUT', #TableCount OUTPUT
SELECT #TableCount
GO
You could instead have the dynamic query return the result as a row set, which you would then insert into a table variable (could be a temporary or ordinary table as well) using the INSERT ... EXEC syntax. Afterwards you can just read the saved value into a variable using SELECT #var = ...:
DECLARE #rowcount TABLE (Value int);
INSERT INTO #rowcount
EXEC('SELECT COUNT(*) ' + #sqlBody);
SELECT #ActualNumberOfResults = Value FROM #rowcount;
Late in the day, but I found this method much simpler:
-- test setup
DECLARE #sqlBody nvarchar(max) = N'SELECT MyField FROM dbo.MyTable WHERE MyOtherField = ''x''';
DECLARE #ActualNumberOfResults int;
-- the goods
EXEC sp_executesql #sqlBody;
SET #ActualNumberOfResults = ##ROWCOUNT;
SELECT #ActualNumberOfResults;
After executing your actual query store the result of ##ROWCOUNT in any variable which you can use later.
EXEC sp_executesql 'SELECT TOP 10 FROM ABX'
SET #TotRecord = ##ROWCOUNT into your variable for later use.
Keep in mind that dynamic SQL has its own scope. Any variable declared/modified there will go out of scope after your EXEC or your sp_executesql.
Suggest writing to a temp table, which will be in scope to your dynamic SQL statement, and outside.
Perhaps put it in your sqlBottom:
CREATE TABLE ##tempCounter(MyNum int);
EXEC('SELECT #ActualNumberOfResults = COUNT(*) ' + #sqlBody +
'; INSERT INTO ##tempCounter(MyNum) VALUES(#ActualNumberOfResults);');
SELECT MyNum FROM ##tempCounter;
You can use output variable in SP_EXECUTESQL
DECLARE #SQL NVARCHAR(MAX);
DECLARE #ParamDefinition NVARCHAR(100) = '#ROW_SQL INT OUTPUT'
DECLARE #AFFECTED_ROWS INT;
SELECT
#SQL = N'SELECT 1 UNION ALL SELECT 2'
SELECT #SQL += 'SELECT #ROW_SQL = ##ROWCOUNT;';
EXEC SP_EXECUTESQL #SQL, #ParamDefinition, #ROW_SQL=#AFFECTED_ROWS OUTPUT;
PRINT 'Number of affected rows: ' + CAST(#AFFECTED_ROWS AS VARCHAR(20));
Ouput:
SQL2.sql: Number of affected rows: 2
Thanks Jesus Fernandez!
The only problem with the answers that create temporary tables (either using "DECLARE #rowcount TABLE" or "CREATE TABLE ##tempCounter(MyNum int)") is that you're having to read all the affected records off disk into memory. If you're expecting a large number of records this may take some time.
So if the answer is likely to be large the "use sp_executesql and an output parameter" solution is a more efficient answer. And it does appear to work.