SQL Server 2008: Insert variable into DML statements using Stored Procedure - sql

I have the following procedure:
CREATE PROCEDURE [dbo].[Test1]
AS
BEGIN
INSERT INTO [My_Database].[My_Schema].[My_Table]
(...lists columns...)
SELECT ... lots of columns from joined query...
END
Instead of hardcoding "[My_Database].[My_Schema]", I now want to select it as a variable from a predefined table like this:
CREATE PROCEDURE [dbo].[Test1]
AS
BEGIN
SELECT #myDB = [My_DB] FROM [my_custom_table]
--INSERT INTO [My_Database].[My_Schema].[My_Table]
INSERT INTO #myDB.[My_Table]
(...lists columns...)
SELECT ... lots of columns from joined query...
END
It does not work if I use it like above. I need to use:
EXEC sp_executesql (entire_sql_statement_in_quotes)
My problem is that I have a lot of these procedures to change to using a variable instead of being hardcoded. It will take forever to convert each statement to a long string.
Is there some other way to do it? What am I missing?
Regards

One idea, you could drop and recreate a synonym using dynamic SQL at the beginning of each procedure, then you can leave each Insert statement as Insert Into MySynonym
DROP SYNONYM MySynonym -- Must create it first before running this bit!
DECLARE #sql nvarchar(max)
SET #SQL = 'CREATE SYNONYM MySynonym
FOR ' + #myDB + '.test1'
EXEC sp_Executesql #sql
INSERT INTO MySynonym
SELECT ...
This would give you a peice of code you could copy paste into each SP. If the table you are inserting into is different for each SP, you could declare that too and build it into your CREATE SYNONYM statement
SET #SQL = 'CREATE SYNONYM MySynonym
FOR ' + #myDB + '.' + #MyTable
to Truncate each table first you would need to use DynamicSQL also, as you cannot delete on a synonym
SET #SQL = 'Truncate Table ' + #MyTable
EXEC sp_Executesql #sql

Related

Inserting into a user-defined table type via a dynamic query in SQL Server?

I have a user-defined table type tyAnalysisNumbers. I need to populate my user defined data type within a stored procedure with a SELECT statement and I am struggling to get that working within my stored procedure.
The following ways I have tried do not work
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM ' + #someTable);
I get this error:
An INSERT EXEC statement cannot be nested
I am unsure how to insert into my custom table via a select statement.
Can anyone help me accomplish this?
An INSERT EXEC statement cannot be nested
Above error is self explanatory. Please look at below scenario:
For example, we have one procedure which inserts data in table type and return result.
CREATE PROCEDURE uspInsertData1
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM someTable');
select * from #MyTable
END
Now, let's say we have another procedure which will call above procedure and again insert data in another table.
CREATE PROCEDURE uspInsertData2
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO sometable
EXEC uspInsertData1
END
Now, if you execute 1st procedure it will work fine but if you execute second procedure you will get this error.
An INSERT EXEC statement cannot be nested.
Because now you have nested EXEC statements.
I suggest to finish your work in single stored procedure if possible.
Try it like this:
DECLARE #MyTable tyAnalysisNumbers;
SELECT * INTO #Temp FROM #MyTable;
DECLARE #tblName AS SYSNAME = (SELECT name FROM sys.tables WHERE name = #someTable);
EXEC ('INSERT INTO #Temp SELECT * FROM ' + #tblName);
This also addresses the SQL Injection problem.

How to use a variable in "Select [some calculations] insert into #NameOfTheTableInThisVariable"?

I have a procedure in which there are calculations being done and the final result is inserted into a permanent table. I want to remove the permanent table and I cannot use Temp table as well. So i want to use a dynamic table name, which is stored in a variable:
Current scenario:
Insert into xyz_table
Select col1,col2,sum(col3)
from BaseTable
(In reality, there are lot of columns and a lot of calculations)
What I want:
Select col1,col2,sum(col3) into #DynamicTableName
from BaseTable
where the name of the table would be dynamic in nature i.e.,
#DynamicTableName = 'xyz ' + cast(convert(date,getdate()) as nvarchar)+' '+convert(nvarchar(5),getdate(),108)
It will have date and time in its name every time the procedure is run.
I want to use this name in the "Select * into statement"
How can I achieve this?
i tried it with the some short code. But since my procedure has a lot of calculations and UNIONS , I cannot use that code for this. Any help would be appreciated.
declare #tablename nvarchar(30)= 'xyz ' + cast(convert(date,getdate()) as nvarchar)+' '+convert(nvarchar(5),getdate(),108)
declare #SQL_Statement nvarchar(100)
declare #SQL_Statement2 nvarchar(100)
declare #dropstatement nvarchar(100)
SET #SQL_Statement = N'SELECT * Into ' +'['+#tablename +'] '+'FROM '+ 'dimBranch'
print #SQL_Statement
EXECUTE sp_executesql #SQL_Statement
SET #SQL_Statement= N'select * from ' + '['+#tablename + '] '
print #SQL_Statement
EXECUTE sp_executesql #SQL_Statement
set #dropstatement = 'DROP TABLE' + '['+#tablename + '] '
PRINT #dropstatement
exec sp_executesql #dropstatement
Reason why I want this is because I use this procedure in ETL job as well as in SSRS report. And if someone runs the package and the SSRS report at the same time, the incorrect or weird data gets stored in the table. Therefore I need a dynamic name of the table with date and time.
You can't parameterize an identifier in SQL, only a value
--yes
select * from table where column = #value
--no
select * from #tablename where #columnname = #value
The only thin you can do to make these things dynamic is to build an sql string and execute it dynamically, but your code is already doing this with sp_executesql
More telling is your complaint at the bottom of your question, that if the procedure is invoked simultaneously it gives problems. Perhaps you should consider using local table variables for temporary data storage that the report is using rather than pushing data back into the db
DECLARE #temp TABLE(id INT, name varchar100);
INSERT INTO #temp SELECT personid, firstname FROM person;
-- work with temp data
select count(*) from #temp;
--when #temp goes out of scope it is lost,
--no other procedure invoked simultaneously can access this procedure'a #temp
Consider a local temp table, which is automatically session scoped without the need for dynamic SQL. For example:
SELECT *
INTO #YourTempTable
FROM dimBranch;
The local temp table will automatically be dropped when the proc completes so there is no need for an explict drop in the proc code.

Execute dynamic sql for all rows in a result set without a loop

I have a query that generates a query for each row in a table.
For example:
select ' create proc ['+[ProcName]+'] as
print '''+[ProcName]+''''
from MyTable
The results of this query will give me a sql statement I can execute for every row of data in the table.
CREATE PROC [proc_1]
AS
PRINT 'proc_1'
--
CREATE PROC [proc_2]
AS
PRINT 'proc_2'
etc.
Is it possible to execute every row in my result set without having to implement some form of cursor/loop?
You can concatenate all column values in sql pass variable by many ways
as examples: XMLPATH, STUFF or COALESCE, with some manipulation with string.
but still getting an error
The Main Issue for This task is Go
Go is Not-Valid T-SQL
so if you tried execute dynamic sql contains Go, the next error will be raised:-
Msg 102, Level 15, State 1, Line 4 Incorrect syntax near 'go'.
After surfing the stackoverflow , I get the resolved here:-
Execute Dynamic Query with go in sql
so Get the next demo (after applying the above link with my trials):-
Demo:-
-- Try to create 4 procedures proc_1, proc_2 , proc_3 and proc_4
Create database Demo
go
use Demo
go
Create table MyTable (procName varchar (200))
go
insert into MyTable values ('proc_1')
go
insert into MyTable values ('proc_2')
go
insert into MyTable values ('proc_3')
go
insert into MyTable values ('proc_4')
go
declare #Query nvarchar(max)
SELECT #Query = isnull(#Query,'') + 'create proc ['+[ProcName]+'] as
print '''+[ProcName]+''''+ char (10) + '
Go
'
FROM MyTable
--print #Query
SET #Query = 'EXEC (''' + REPLACE(REPLACE(#Query, '''', ''''''), 'GO', '''); EXEC(''') + ''');'
EXEC (#Query)
Result:-
you can declare a variable, store the queries (seperates) inside it and execute it
DECLARE #strQuery Varchar(MAX)
SET #strQuery = ''
select #strQuery = #strQuery +
'EXEC('' create proc [' + [ProcName] + ']
as
print ''''' + [ProcName] + '''''
'')'
from MyTable
EXEC(#strQuery)
--To view your query
PRINT(#strQuery)
Note: i used Exec command for each procedure because they cannot be executed at the same time in a query

Table name variable in MS SQL query

I have dynamically created tables, like XXX_JOURNAL.
Where XXX - is table prefix (variable), and _JOURNAL - is constant in table name.
I need create UPDATE trigger on database, not on particular table, and use table name (prefix) as variable:
CREATE TRIGGER triggerName ON %_JOURNAL
FOR UPDATE
AS
UPDATE XXX_JOURNAL
SET COMPANY_ID = LEFT(tableName,3) //tableName = current table (XXX_JOURNAL)
WHERE ID = ID FROM inserted
So here I have two difficulties:
How to create one trigger for all tables LIKE %_JOURNAL?
How to use table name as the keyword for current table?
I know there are a lot of mistakes in syntax. For example, I cannot use '%_JOURNAL' as table name on trigger creation. It's just for explanation, that I need create one trigger for all dynamically created tables in future.
Any ideas?
You can use stored procedure with dynamic SQL:
CREATE PROCEDURE TriggerCreationForJournals
#XXX as nvarchar(3)
AS
BEGIN
DECLARE #sql nvarchar(max),
#triggerName nvarchar(max) = #XXX + N'_JOURNAL_UPDATE',
#objectCheck int,
#checkSQL nvarchar(max),
#params nvarchar(max) = N'#objectCheck int OUTPUT'
SELECT #checkSQL = N'SELECT #objectCheck = OBJECT_ID(N'''+#triggerName+''')'
EXEC sp_executesql #checkSQL, #params, #objectCheck = #objectCheck OUTPUT
IF #objectCheck IS NULL
BEGIN
SELECT #sql = N'
CREATE TRIGGER '+QUOTENAME(#triggerName)+' ON ['+#XXX+'_JOURNAL]
FOR UPDATE
AS
UPDATE x
SET COMPANY_ID = '''+#XXX+'''
FROM ['+#XXX+'_JOURNAL] x
INNER JOIN inserted i
ON i.ID = x.ID'
EXEC sp_executesql #sql
END
ELSE
BEGIN
PRINT 'Trigger '+QUOTENAME(#triggerName)+' already exists'
END
END
Then run this:
DECLARE #sql nvarchar(max)
SELECT #sql = (
SELECT 'EXEC TriggerCreationForJournals '''+LEFT([name],3) +''';' +CHAR(10)
FROM sys.tables
WHERE [name] LIKE '%JOURNAL'
FOR XML PATH('')
)
EXEC sp_executesql #sql
To create triggers for all tables.
In #sql there will be query like:
EXEC TriggerCreationForJournals 'AFG';
EXEC TriggerCreationForJournals 'DFG';
The purpose of stored procedure is to check if trigger on table exists - if so skip its creation, you can modify the SP to drop them if exists.
The second part is a creation of script and running the SP for all tables you need.
Hope, this answer helps you with your questions.

How to BULK INSERT a file into a *temporary* table where the filename is a variable?

I have some code like this that I use to do a BULK INSERT of a data file into a table, where the data file and table name are variables:
DECLARE #sql AS NVARCHAR(1000)
SET #sql = 'BULK INSERT ' + #tableName + ' FROM ''' + #filename + ''' WITH (CODEPAGE=''ACP'', FIELDTERMINATOR=''|'')'
EXEC (#sql)
The works fine for standard tables, but now I need to do the same sort of thing to load data into a temporary table (for example, #MyTable). But when I try this, I get the error:
Invalid Object Name: #MyTable
I think the problem is due to the fact that the BULK INSERT statement is constructed on the fly and then executed using EXEC, and that #MyTable is not accessible in the context of the EXEC call.
The reason that I need to construct the BULK INSERT statement like this is that I need to insert the filename into the statement, and this seems to be the only way to do that. So, it seems that I can either have a variable filename, or use a temporary table, but not both.
Is there another way of achieving this - perhaps by using OPENROWSET(BULK...)?
UPDATE:
OK, so what I'm hearing is that BULK INSERT & temporary tables are not going to work for me. Thanks for the suggestions, but moving more of my code into the dynamic SQL part is not practical in my case.
Having tried OPENROWSET(BULK...), it seems that that suffers from the same problem, i.e. it cannot deal with a variable filename, and I'd need to construct the SQL statement dynamically as before (and thus not be able to access the temp table).
So, that leaves me with only one option which is to use a non-temp table and achieve process isolation in a different way (by ensuring that only one process can be using the tables at any one time - I can think of several ways to do that).
It's annoying. It would have been much more convenient to do it the way I originally intended. Just one of those things that should be trivial, but ends up eating a whole day of your time...
You could always construct the #temp table in dynamic SQL. For example, right now I guess you have been trying:
CREATE TABLE #tmp(a INT, b INT, c INT);
DECLARE #sql NVARCHAR(1000);
SET #sql = N'BULK INSERT #tmp ...' + #variables;
EXEC master.sys.sp_executesql #sql;
SELECT * FROM #tmp;
This makes it tougher to maintain (readability) but gets by the scoping issue:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'CREATE TABLE #tmp(a INT, b INT, c INT);
BULK INSERT #tmp ...' + #variables + ';
SELECT * FROM #tmp;';
EXEC master.sys.sp_executesql #sql;
EDIT 2011-01-12
In light of how my almost 2-year old answer was suddenly deemed incomplete and unacceptable, by someone whose answer was also incomplete, how about:
CREATE TABLE #outer(a INT, b INT, c INT);
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SET NOCOUNT ON;
CREATE TABLE #inner(a INT, b INT, c INT);
BULK INSERT #inner ...' + #variables + ';
SELECT * FROM #inner;';
INSERT #outer EXEC master.sys.sp_executesql #sql;
It is possible to do everything you want. Aaron's answer was not quite complete.
His approach is correct, up to creating the temporary table in the inner query. Then, you need to insert the results into a table in the outer query.
The following code snippet grabs the first line of a file and inserts it into the table #Lines:
declare #fieldsep char(1) = ',';
declare #recordsep char(1) = char(10);
declare #Lines table (
line varchar(8000)
);
declare #sql varchar(8000) = '
create table #tmp (
line varchar(8000)
);
bulk insert #tmp
from '''+#filename+'''
with (FirstRow = 1, FieldTerminator = '''+#fieldsep+''', RowTerminator = '''+#recordsep+''');
select * from #tmp';
insert into #Lines
exec(#sql);
select * from #lines
Sorry to dig up an old question but in case someone stumbles onto this thread and wants a quicker solution.
Bulk inserting a unknown width file with \n row terminators into a temp table that is created outside of the EXEC statement.
DECLARE #SQL VARCHAR(8000)
IF OBJECT_ID('TempDB..#BulkInsert') IS NOT NULL
BEGIN
DROP TABLE #BulkInsert
END
CREATE TABLE #BulkInsert
(
Line VARCHAR(MAX)
)
SET #SQL = 'BULK INSERT #BulkInser FROM ''##FILEPATH##'' WITH (ROWTERMINATOR = ''\n'')'
EXEC (#SQL)
SELECT * FROM #BulkInsert
Further support that dynamic SQL within an EXEC statement has access to temp tables outside of the EXEC statement. http://sqlfiddle.com/#!3/d41d8/19343
DECLARE #SQL VARCHAR(8000)
IF OBJECT_ID('TempDB..#BulkInsert') IS NOT NULL
BEGIN
DROP TABLE #BulkInsert
END
CREATE TABLE #BulkInsert
(
Line VARCHAR(MAX)
)
INSERT INTO #BulkInsert
(
Line
)
SELECT 1
UNION SELECT 2
UNION SELECT 3
SET #SQL = 'SELECT * FROM #BulkInsert'
EXEC (#SQL)
Further support, written for MSSQL2000 http://technet.microsoft.com/en-us/library/aa175921(v=sql.80).aspx
Example at the bottom of the link
DECLARE #cmd VARCHAR(1000), #ExecError INT
CREATE TABLE #ErrFile (ExecError INT)
SET #cmd = 'EXEC GetTableCount ' +
'''pubs.dbo.authors''' +
'INSERT #ErrFile VALUES(##ERROR)'
EXEC(#cmd)
SET #ExecError = (SELECT * FROM #ErrFile)
SELECT #ExecError AS '##ERROR'
http://msdn.microsoft.com/en-us/library/ms191503.aspx
i would advice to create table with unique name before bulk inserting.