I have a temp table like
DECLARE #t TABLE (a BIGINT)
I want to do this
SELECT * FROM #t
but I need to do it by EXEC query, and this doesn't work
DECLARE #query AS NVARCHAR(100) = 'SELECT * FROM #t'
EXEC(#query)
how can I create a custom query to select the temp table?
Thanks.
This is because EXEC statement will execute the Statements in new Session. And the Table variables scope is fixed to the batch of statement.
Since you declared the Table Variable out side of the Session , you can't access the table variable in EXEC statement.
So you need to DECLARE ,INSERT, UPDATE, SELECT table variables in the Dynamic Code itself.
DECLARE #query NVARCHAR(MAX)='';
SELECT #query ='
DECLARE #t TABLE
(
a BIGINT
)'
SELECT #query += 'SELECT * FROM #t'
EXEC(#query)
Solution 2:
The another Solution is to create Global Temporary Table which we can create using ##. And Global Temporary tables scope is limited to Database all connections.
CREATE TABLE ##TABLE1
(
a BIGINT
)
DECLARE #query NVARCHAR(MAX)='';
SELECT #query += 'SELECT * FROM ##TABLE1'
EXEC(#query)
But be aware if another user execute the same Query, there might be conflict with the same name creation.
Try hash table as below:
DECLARE #tblTest AS Table
(
Name VARCHAR(50)
)
insert into #tblTest values('Sandip')
insert into #tblTest values('AAA')
IF OBJECT_ID('tempdb..#tblTest') IS NOT NULL
DROP TABLE #tblTest
SELECT * INTO #tblTest FROM #tblTest
DECLARE #query AS NVARCHAR(100) = 'SELECT * FROM #tblTest'
EXEC (#query)
Output:
CREATE TYPE MyTable AS TABLE
(
a BIGINT
);
GO
DECLARE #T AS MyTable;
EXEC sp_executesql
N'SELECT * FROM #T',
N'#T MyTable READONLY',
#T=#T
please try this
Related
BACKGROUND
I have a procedure that has INSERT INTO ... EXEC (#sql) from dynamic SQL.
This procedure's results is INSERTed INTO table outside of procedure. When I attempt this, I get get an error:
[S0001][8164] An INSERT EXEC statement cannot be nested.
This error is discussed in other questions, but for inner procedures' calls instead of dynamic SQL:
Errors: "INSERT EXEC statement cannot be nested." and "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." How to solve this?
An insert exec statement cannot be nested
Example with error:
-- =================================
-- table with test data
-- =================================
CREATE TABLE dbo.TestInsertIntoDynamicData1
(
data nvarchar(max)
)
INSERT INTO dbo.TestInsertIntoDynamicData1
VALUES ('one1'), ('two1'), ('three1')
GO
-- =================================
-- another table with test data
-- =================================
CREATE TABLE dbo.TestInsertIntoDynamicData2
(
data nvarchar(max)
)
INSERT INTO dbo.TestInsertIntoDynamicData2
VALUES ('one2'), ('two2'), ('three2')
GO
-- =================================
-- procedure with dynamic query
-- =================================
CREATE PROCEDURE dbo.TestInsertIntoDynamicProc
#TableName nvarchar(100)
AS
BEGIN
DECLARE #Results table(
data nvarchar(max)
)
DECLARE #sql nvarchar(max)
SET #sql = '
SELECT data
FROM dbo.' + #TableName + ';
'
-- FIRST INSERT INTO ... EXEC ...
INSERT INTO #Results -- this INSERT is required for example
EXEC (#sql)
SELECT *
FROM #Results;
END
GO
-- =================================
-- CALL
-- =================================
DECLARE #ResultsOfProc table(
data nvarchar(max)
)
-- SECOND INSERT INTO ... EXEC ...
INSERT INTO #ResultsOfProc (data)
EXEC dbo.TestInsertIntoDynamicProc #TableName = 'TestInsertIntoDynamicData2'
SELECT *
FROM #ResultsOfProc;
GO
DROP TABLE dbo.TestInsertIntoDynamicData1
DROP TABLE dbo.TestInsertIntoDynamicData2
DROP PROCEDURE dbo.TestInsertIntoDynamicProc
https://stackoverflow.com/a/2917775/7573844
QUESTION
How can we get around this error?
Move INSERT INTO to dynamic query.
Refactor table variable to temp table in order to use it in dynamic query.
Fixed procedure:
CREATE PROCEDURE dbo.TestInsertIntoDynamicProcFixed
#TableName nvarchar(100)
AS
BEGIN
CREATE TABLE #Results ( -- refactor to temp table
data nvarchar(max)
)
DECLARE #sql nvarchar(max)
SET #sql = '
INSERT INTO #Results -- move INSERT to here
SELECT data
FROM dbo.' + #TableName + ';
'
-- FIRST INSERT INTO ... EXEC ...
EXEC (#sql)
SELECT *
FROM #Results;
END
GO
I'm having trouble executing the below piece of code, it's giving me an error as below:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '#ST'.
I can try implementing the login using dynamic SQL, but wanted to try the sp_executesql method. Please let me know if I'm having some syntax error or I'm not supposed to pass table names as parameters?
DECLARE #SQL NVARCHAR(4000)= '';
SET #SQL = N'--INSERT INTO #missingkeys ( SOURCE_KEY,[ROWCOUNT] )
SELECT S.[SOURCE_KEY], COUNT(1) AS [ROWCOUNT] FROM (SELECT DISTINCT #SK AS [SOURCE_KEY]
FROM [PDA].#ST ) AS S
LEFT JOIN [PDA].#MT AS T
ON T.[SOURCE_KEY] = S.[SOURCE_KEY]
GROUP BY S.[SOURCE_KEY]';
DECLARE #SOURCETABLE NVARCHAR(255)= 'FACT';
DECLARE #SOURCE_KEY NVARCHAR(255)= 'KEY', #MAP_TABLE NVARCHAR(255)= 'DimMap';
EXEC sp_executesql
#SQL,
N'#SK nvarchar(255), #ST nVARCHAR(255), #MT nVARCHAR(255)',
#SK = #SOURCE_KEY,
#ST = #SOURCETABLE,
#MT = #MAP_TABLE;
You can't have columns as parameters, same for any object name (table, stored procedure, ...).
You will have to make the statement dynamic, i.e. format the column name in the SQL string:
SET #SQL =
N'SELECT '+
'S.[SOURCE_KEY],'+
'COUNT(1) AS [ROWCOUNT] '+
'FROM ('+
'SELECT DISTINCT '+
QUOTENAME(#SK)+' AS [SOURCE_KEY] '+
'...'; -- the rest of your statement
PS: Use QUOTENAME to escape object names to avoid SQL Injection.
You can pass tables if you write the data to an identical user-defined table type. The parameter must be READONLY:
CREATE TYPE [dbo].[t] AS TABLE([a] [int] NOT NULL PRIMARY KEY CLUSTERED)
create table #t (a int)
insert into #t values (1), (2), (3)
exec sp_executesql N'select * from #t'
declare #t t
insert into #t select a from #t
exec sp_executesql N'Select * from #p1', N'#p1 t readonly', #t
I have some tables for storing different file information, like thumbs, images, datasheets, ...
I'm writing a stored procedure to retrieve filename of a specific ID. something like:
CREATE PROCEDURE get_file_name(
#id int,
#table nvarchar(50)
)as
if #table='images'
select [filename] from images
where id = #id
if #table='icons'
select [filename] from icons
where id = #id
....
How can I rewrite this procedure using case when statement or should I just use table name as variable?
You can't use case .. when to switch between a table in the FROM clause (like you can in a conditional ORDER BY). i.e. so the following:
select * from
case when 1=1
then t1
else t2
end;
won't work.
So you'll need to use dynamic SQL. It's best to parameterize the query as far as possible, for example the #id value can be parameterized:
-- Validate #table is E ['images', 'icons', ... other valid names here]
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'select [filename] from **TABLE** where id = #id';
SET #sql = REPLACE(#sql, '**TABLE**', #table);
sp_executesql #sql, N'#id INT', #id = #id;
As with all dynamic Sql, note that unparameterized values which are substituted into the query (like #table), make the query vulnerable to Sql Injection attacks. As a result, I would suggest that you ensure that #table comes from a trusted source, or better still, the value of #table is compared to a white list of permissable tables prior to execution of the query.
Just build SQL string in another variable and EXECUTE it
DECLARE #sql AS NCHAR(500)
SET #sql=
'SELECT [filename] '+
' FROM '+#table+
' WHERE id = #id'
EXECUTE(#sql)
CREATE PROCEDURE get_file_name(
#id int,
#table nvarchar(50)
)as
DECLARE #SQL nvarchar(max);
SET #SQL = 'select [filename] from ' + #table + ' where id = ' + #id
EXECUTE (#SQL)
tables:
create table TabA
(ID int, Name varchar(20))
insert into TabA
select 1,'ABC' union
select 2,'DEF' union
select 3,'GHD'
create table TabB
(ID int, Name varchar(20))
insert into TabA
select 1,'XYZ' union
select 2,'STF' union
select 3,'LDZ'
create table status
(Result1 int,Result2 int )
Create table query(query1 varchar(1000),query2 varchar(1000))
Insert into query(query1,query2)
select '''select COUNT(*) from TabA''','''select COUNT(* )from TabB'''
select * from query
procedure:
create Procedure [dbo].spStatus
AS
BEGIN
SET NOCOUNT ON;
Declare #sqlString1 nvarchar(1000)
,#sqlString2 nvarchar(1000)
,#col_value1 varchar(256)
,#col_value2 varchar(256)
select #sqlString1 = query1
, #sqlString2 =query2
from Query
EXEC sp_executesql
#query=#sqlString1, --sql string is your full select statement
#params = N'#col_Value1 varchar(256) OUTPUT',
#col_Value1 = #col_Value1 OUTPUT
print(#sqlString1)
-- #sqlString2, --sql string is your full select statement
--#params = N'#col_Value2 varchar(256) OUTPUT',
-- #col_Value2 = #col_Value2 OUTPUT
Insert Into dbo.Status(Result1,Result2 )
Values(#col_Value1,#col_Value2)
End
It works if we use #query=#sqlString1 only but I want both statement #query=#sqlString1,#query=#sqlString2 should execute together.
Please help how can we use both statement to execute?
Thanks in Advance
Did you mean:
SET #sqlString1 = #sqlString1 + ';' + #sqlString2;
EXEC sp_executesql #query = #sqlString1 --...
Concat the two queries together with a + (#query=#sqlString1 + '; ' + #sqlString2)
Then use two variables to capture the two counts into output variables
OR
Insert into query(query1,query2)
EXEC sp_executesql 'SELECT ( select COUNT(*) from TabA ) AS query1, ( select COUNT(*)from TabB ) AS query2'
... but really and truly dynamic SQL isn't needed for that at all.
try this:
--add this
DECLARE #SQL nvarchar(max)
SET #SQL=ISNULL(#sqlString1,'')+';'+ISNULL(#sqlString2,'')
--change this
EXEC sp_executesql #query=#SQL
,#params = N'#col_Value1 varchar(256) OUTPUT'
,#col_Value1 = #col_Value1 OUTPUT
The code is as follows:
ALTER PROCEDURE dbo.pdpd_DynamicCall
#SQLString varchar(4096) = null
AS
Begin
create TABLE #T1 ( column_1 varchar(10) , column_2 varchar(100) )
insert into #T1
execute ('execute ' + #SQLString )
select * from #T1
End
The problem is that I want to call different procedures that can give back different columns.
Therefore I would have to define the table #T1 generically.
But I don't know how.
Can anyone help me on this problem?
Try:
SELECT into #T1 execute ('execute ' + #SQLString )
And this smells real bad like an sql injection vulnerability.
correction (per #CarpeDiem's comment):
INSERT into #T1 execute ('execute ' + #SQLString )
also, omit the 'execute' if the sql string is something other than a procedure
You can define a table dynamically just as you are inserting into it dynamically, but the problem is with the scope of temp tables. For example, this code:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE #T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO #T1 (Col1) VALUES ('This will not work.')
SELECT * FROM #T1
will return with the error "Invalid object name '#T1'." This is because the temp table #T1 is created at a "lower level" than the block of executing code. In order to fix, use a global temp table:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE ##T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO ##T1 (Col1) VALUES ('This will work.')
SELECT * FROM ##T1
Hope this helps,
Jesse
Be careful of a global temp table solution as this may fail if two users use the same routine at the same time as a global temp table can be seen by all users...
create a global temp table with a GUID in the name dynamically. Then you can work with it in your code, via dyn sql, without worry that another process calling same sproc will use it. This is useful when you dont know what to expect from the underlying selected table each time it runs so you cannot created a temp table explicitly beforehand. ie - you need to use SELECT * INTO syntax
DECLARE #TmpGlobalTable varchar(255) = 'SomeText_' + convert(varchar(36),NEWID())
-- select #TmpGlobalTable
-- build query
SET #Sql =
'SELECT * INTO [##' + #TmpGlobalTable + '] FROM SomeTable'
EXEC (#Sql)
EXEC ('SELECT * FROM [##' + #TmpGlobalTable + '] ')
EXEC ('DROP TABLE [##' + #TmpGlobalTable + ']')
PRINT 'Dropped Table ' + #TmpGlobalTable
INSERT INTO #TempTable
EXEC(#SelectStatement)
Try Below code for creating temp table dynamically from Stored Procedure Output using T-SQL
declare #ExecutionName varchar(1000) = 'exec [spname] param1,param2 '
declare #sqlStr varchar(max) = ''
declare #tempTableDef nvarchar(max) =
(
SELECT distinct
STUFF(
(
SELECT ','+a.[name]+' '+[system_type_name]
+'
' AS [text()]
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) a
ORDER BY a.column_ordinal
FOR XML PATH ('')
), 1, 1, '') tempTableDef
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) b
)
IF ISNULL(#tempTableDef ,'') = '' RAISERROR( 'Invalid SP Configuration. At least one column is required in Select list of SP output.',16,1) ;
set #tempTableDef='CREATE TABLE #ResultDef
(
' + REPLACE(#tempTableDef,'
','') +'
)
INSERT INTO #ResultDef
' + #ExecutionName
Select #sqlStr = #tempTableDef +' Select * from #ResultDef '
exec(#sqlStr)
DECLARE #EmpGroup INT =3 ,
#IsActive BIT=1
DECLARE #tblEmpMaster AS TABLE
(EmpCode VARCHAR(20),EmpName VARCHAR(50),EmpAddress VARCHAR(500))
INSERT INTO #tblEmpMaster EXECUTE SPGetEmpList #EmpGroup,#IsActive
SELECT * FROM #tblEmpMaster
CREATE PROCEDURE dbo.pdpd_DynamicCall
AS
DECLARE #SQLString_2 NVARCHAR(4000)
SET NOCOUNT ON
Begin
--- Create global temp table
CREATE TABLE ##T1 ( column_1 varchar(10) , column_2 varchar(100) )
SELECT #SQLString_2 = 'INSERT INTO ##T1( column_1, column_2) SELECT column_1 = "123", column_2 = "MUHAMMAD IMRON"'
SELECT #SQLString_2 = REPLACE(#SQLString_2, '"', '''')
EXEC SP_EXECUTESQL #SQLString_2
--- Test Display records
SELECT * FROM ##T1
--- Drop global temp table
IF OBJECT_ID('tempdb..##T1','u') IS NOT NULL
DROP TABLE ##T1
End
Not sure if I understand well, but maybe you could form the CREATE statement inside a string, then execute that String? That way you could add as many columns as you want.