Passing table name in sql stored procedure - tsql

Is it possible to pass the table name as input parameter to the stored procedure?
For example:
create procedure test
#tablename char(10)
as
begin
select * from #tablename
end
go
I know this does not work. So what is the best way if I want to pass the table name into the stored procedure?
Many thanks

The safest way to do this is via a view.
Create a view which unions all the tables you may wish to access (and which must all have the same column structure), and prefix the rows with the table name.
CREATE VIEW MultiTable
AS
SELECT 'table1' AS TableName, * FROM table1
UNION ALL
SELECT 'table2' AS TableName, * FROM table2
UNION ALL
SELECT 'table3' AS TableName, * FROM table3
Your stored procedure can now filter on the table name:
CREATE PROCEDURE test
#TableName varchar(100)
AS
SELECT * FROM MultiTable WHERE TableName = #TableName
This is safer than using dynamic SQL creation and execution.

You would need to use dynamic SQL, but you need to be aware of potential sql injection risks you open yourself up to as if #tablename contained something dodgy, you could end up in a world of pain.
e.g.
-- basic check to see if a table with this name exists
IF NOT EXISTS(SELECT * FROM sys.tables WHERE name = #tablename)
RETURN
DECLARE #sql NVARCHAR(100)
SET #sql = 'SELECT * FROM ' + QUOTENAME(#tablename)
EXECUTE(#sql)
You need to be very careful with this approach, make sure you don't open up a can of security worms.
My other concern is that you may be trying to make generic data access sprocs which is usually a bad idea. Obviously I don't know your use case.

DECLARE #Name VARCHAR(50)
SET #Name='Company'
EXEC('SELECT * from ' + #Name )
use this way to get record from database.

Related

Facing Problem like "Cannot select from or insert/update variable '#TableName' because it is not a table variable." in Sybase ASE

I am creating one store proc that will get some tablename as a parameter and it will do
"select #TableName from #TableName"
But Sybase ASE sql not allowing me to do that. I am getting this message "Cannot select from or insert/update variable '#TableName' because it is not a table variable."
Here is my store proc mentioned below
CREATE PROC Test_result #TableName VARCHAR(40)
as
BEGIN
CREATE TABLE #Results (TableName nvarchar(370))
INSERT INTO #Results select #TableName from #TableName
select * from #Results
END
EXEC Test_result 'sometablename'
This will simulate my actual problem. I want to insert a tablename into a Results table if it match some condition(I haven't mention that here because I don't want to confuse you).
**
**Note: I want to do a quick select query from a TableName which I passed to the store proc.I don't want to create again the table
structure because that store proc may get another tablename whose
table DDL is different **
**
Could anyone provide some alternative or any solution on it ?
Sorry for delay in response. I have found myself a workaround for that which I would like to share.
INSERT INTO #Results select #TableName from #TableName
To make this working, use a variable to store this query and execute using EXEC statement in sybase.
The workaround will be,
BEGIN
SET #sqlquery='INSERT INTO #Results select #TableName from #TableName '
EXEC(#sqlquery)
END
This solved my problem as #tablename variable we can't directly used to replace the value of a table.
If the objective is to insert the value of #TableName into #Results then either of the following should suffice:
INSERT INTO #Results select #TableName
INSERT INTO #Results values (#TableName)
If the intent is to insert #TableName into #Results but only if there's a user table with this name in the current database then try:
INSERT INTO #Results select name from sysobjects where type = 'U' and name = #TableName
If this doesn't answer the question then please update the question with more details as well as some examples of #TableName values that do and do not work.

How to insert dynamic sql result into temp table without knowing the columns in advance

I am trying to insert result of dynamic sql into temp table. Important thing is i dont know the column names in advance. As per the SO suggestion the following should work
INSERT into #T1 execute ('execute ' + #SQLString )
also, omit the EXECUTE if the sql string is something other than a procedure.
However this is not working on SQL 2017
CREATE TABLE Documents(DocumentID INT, Status NVARCHAR(10))
INSERT INTO Documents(DocumentID,Status)
VALUES
(1,'Active'),
(2,'Active'),
(3,'Active'),
(4,'Active')
DECLARE #SQLString NVARCHAR(MAX)
SET #SQLString = 'SELECT * FROM Documents'
INSERT into #T1 execute ('execute ' + #SQLString )
I get error `Invalid object name '#T1'.`
Then i tried by omitting execute
INSERT into #T1 execute (#SQLString)
with same error `Invalid object name '#T1'.`
I should be able to do
SELECT * FROM #T1
You cannot do an INSERT INTO without having the table predefined. But what I believe you are asking is to do a SELECT INTO. I am aware of two ways of doing it. The first uses OPENROWSET, but I believe this has some drawbacks for security purposes. You could do the following:
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT *
INTO #T1
FROM OPENROWSET('SQLNCLI',
'Server=localhost;Trusted_Connection=yes;',
'SELECT * from <YOURDATABASE>.dbo.Documents')
Your second option is to create an inline TVF that will generate the table structure for you. So you could do the following:
CREATE FUNCTION getDocuments()
RETURNS TABLE
AS
RETURN
SELECT * from Documents
GO
SELECT * into #T1 FROM getDocuments()

IF... ELSE... two mutually exclusive inserts INTO #temptable

I need to insert either set A or set B of records into a #temptable, depending on certain condition
My pseudo-code:
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF {some-condition}
SELECT {columns}
INTO #t1
FROM {some-big-table}
WHERE {some-filter}
ELSE
SELECT {columns}
INTO #t1
FROM {some-other-big-table}
WHERE {some-other-filter}
The two SELECTs above are exclusive (guaranteed by the ELSE operator). However, SQL compiler tries to outsmart me and throws the following message:
There is already an object named '#t1' in the database.
My idea of "fixing" this is to create #t1 upfront and then executing a simple INSERT INTO (instead of SELECT... INTO). But I like minimalism and am wondering whether this can be achieved in an easier way i.e. without explicit CREATE TABLE #t1 upfront.
Btw why is it NOT giving me an error on a conditional DROP TABLE in the first line? Just wondering.
You can't have 2 temp tables with the same name in a single SQL batch. One of the MSDN article says "If more than one temporary table is created inside a single stored procedure or batch, they must have different names". You can have this logic with 2 different temp tables or table variable/temp table declared outside the IF-Else block.
Using a Dyamic sql we can handle this situation. As a developoer its not a good practice. Best to use table variable or temp table.
IF 1=2
BEGIN
EXEC ('SELECT 1 ID INTO #TEMP1
SELECT * FROM #TEMP1
')
END
ELSE
EXEC ('SELECT 2 ID INTO #TEMP1
SELECT * FROM #TEMP1
')

Modify table name at runtime

I want to take a backup of a table with the timestamp value linked in the backup table.So that it can be easily figured out to which date this backup belongs to.I am trying something like this which is obviously not working.
Please suggest how to modify table name at runtime.
Scenario:
Insert into original_table+'_'+Convert(varchar(10),GETDATE(),112)
select * from original_table
The output should be:
A table should be created original_table_20141015 with the data.
You can build a SQL string with the new table name, then execute it using sp_executesql.
Example:
DECLARE #sql nvarchar(MAX)
SET #sql = 'SELECT * INTO original_table_' +
CONVERT(varchar(8), GETDATE(), 112) +
' FROM original_table'
EXEC sp_executesql #sql

How to create DDL trigger to all databases in SQL Server 2005 instance

I am going to create a DDL trigger to all databases in SQL Server instance. I'd like to do it in one run instead of many runs for each database.
Below are the two T-SQL statements I need to execute:
-- Create table
use <dbname>
GO
CREATE TABLE dbo.ChangeAttempt
(EventData xml NOT NULL,
AttemptDate datetime NOT NULL DEFAULT GETDATE(),
DBUser char(50) NOT NULL)
GO
-- Create DDL trigger
use <dbname>
GO
CREATE TRIGGER db_trg_ObjectChanges
ON DATABASE
FOR ALTER_PROCEDURE, DROP_PROCEDURE,
ALTER_INDEX, DROP_INDEX,
ALTER_TABLE, DROP_TABLE, ALTER_TRIGGER, DROP_TRIGGER,
ALTER_VIEW, DROP_VIEW, ALTER_SCHEMA, DROP_SCHEMA,
ALTER_ROLE, DROP_ROLE, ALTER_USER, DROP_USER
AS
SET NOCOUNT ON
INSERT dbo.ChangeAttempt
(EventData, DBUser)
VALUES (EVENTDATA(), USER)
GO
My question is: how can I programmaticaly create DDL trigger in one run?
why do you need one run? this is the only way to do it.
Msg 111, Level 15, State 1, Line 2
'CREATE TRIGGER' must be the first statement in a query batch.
run the output generated by this:
DECLARE #DatabaseName varchar(500)
DECLARE #Database_id int
DECLARE #Query varchar(8000)
DECLARE #CRLF char(2)
SET #CRLF=CHAR(13)+CHAR(10)
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
select #Database_id=MIN(database_id) from sys.databases where database_id IN (5,7,8,6)
WHILE #Database_id IS NOT NULL
BEGIN
SELECT #DatabaseName=name from sys.databases where database_id=#Database_id
SET #Query='-- Create table'+#CRLF+#CRLF
+'use '+#DatabaseName+#CRLF
+' GO'+#CRLF
+' CREATE TABLE dbo.ChangeAttempt'+#CRLF
+' (EventData xml NOT NULL,'+#CRLF
+' AttemptDate datetime NOT NULL DEFAULT GETDATE(),'+#CRLF
+' DBUser char(50) NOT NULL)'+#CRLF
+'GO'+#CRLF+#CRLF
+'-- Create DDL trigger '+#CRLF+#CRLF
+'use '+#DatabaseName+#CRLF
+'GO'+#CRLF
+'CREATE TRIGGER db_trg_ObjectChanges'+#CRLF
+'ON DATABASE'+#CRLF
+'FOR ALTER_PROCEDURE, DROP_PROCEDURE,'+#CRLF
+' ALTER_INDEX, DROP_INDEX,'+#CRLF
+' ALTER_TABLE, DROP_TABLE, ALTER_TRIGGER, DROP_TRIGGER,'+#CRLF
+' ALTER_VIEW, DROP_VIEW, ALTER_SCHEMA, DROP_SCHEMA,'+#CRLF
+' ALTER_ROLE, DROP_ROLE, ALTER_USER, DROP_USER'+#CRLF
+'AS'+#CRLF
+'SET NOCOUNT ON'+#CRLF
+'INSERT dbo.ChangeAttempt'+#CRLF
+'(EventData, DBUser)'+#CRLF
+'VALUES (EVENTDATA(), USER)'+#CRLF
+'GO'+#CRLF
PRINT #Query
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
select #Database_id=MIN(database_id) from sys.databases WHERE database_id IN (5,7,8,6) AND database_id>#Database_id
END
EDIT
to determine what databases to generate scripts for do the following:
run this query:
select database_id,name from sys.databases
find all of the databases you want to run the scripts for
change my above script in two places (before loop & at bottom in loop) so all of the database_id that you want are in the following code section:
WHERE database_id IN (AAA,BBB,CCC,DDD,....)
You could use sp_MSforeachdb.
Something like this
sp_MSforeachdb
'
CREATE TABLE ?.dbo.ChangeAttempt
etc. etc. etc.
'
sp_MSforeachdb
'
CREATE TRIGGER ?.dbo.db_trg_ObjectChanges
etc. etc. etc.
'
I haven't tested this, in theory I'd expect it to work though. You want to make sure you exclude the system databases though.