I have a SQL set of instructions that I want to execute across multiple databases. I currently have the following SQL code:
USE Database1
DECLARE #mySourceTable AS [someUserDefinedType];
/*Execute set of operations on Database 1 and #mySourceTable*/
DECLARE #dbList TABLE (DBName nvarchar(50));
INSERT INTO #dbList (DBName)
VALUES('Database2'),('Database3');
DECLARE #dbName nvarchar(50);
DECLARE dbCursor CURSOR FOR SELECT DBName FROM #dbList;
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO #dbName;
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE('USE ' + #dbName + N';
DECLARE #content AS [someUserDefinedType];
INSERT INTO #content (ID)
SELECT ID FROM '+ #mySourceTable + N';
EXECUTE dbo.someProcedure #content;');
FETCH NEXT FROM dbCursor INTO #dbName;
END;
CLOSE dbCursor;
DEALLOCATE dbCursor;
Basically I want to do the following: I have several databases that all have the same [someUserDefinedType] table type (with the same structure) and a procedure named dbo.someProcedure that receives as a parameter a table of said type (the dbo.someProcedure is not the same across databases, it is specific to each). I want to go through the list of provided databases (#dbList) and execute each stored procedure with data from #mySourceTable. I am not sure if the code above is the best approach, it does not work and gives the error:
Must declare the scalar variable "#mySourceTable".
This variable is already declared at the beginning of the script. What am I doing wrong? Is it possible to pass the data from #mySourceTable using a variable and not create another table for it (I really want to avoid that)?
Try something like this:
USE Database1
DECLARE #mySourceTable AS [someUserDefinedType];
/*Execute set of operations on Database 1 and #mySourceTable*/
DECLARE #dbList TABLE (DBName nvarchar(50));
INSERT INTO #dbList (DBName)
VALUES('Database2'),('Database3');
DECLARE #dbName nvarchar(50);
DECLARE dbCursor CURSOR FOR SELECT DBName FROM #dbList;
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO #dbName;
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
WHILE ##FETCH_STATUS = 0
BEGIN
SET #DynamicTSQLStatement = N'
USE ' + #dbName + ';
DECLARE #content AS [someUserDefinedType];
INSERT INTO #content (ID)
SELECT ID FROM #mySourceTable;
EXECUTE dbo.someProcedure #content;';
FETCH NEXT FROM dbCursor INTO #dbName;
EXEC sp_executesql #DynamicTSQLStatement, N'#mySourceTable someUserDefinedType readonly', #mySourceTable = #mySourceTable
END;
CLOSE dbCursor;
DEALLOCATE dbCursor;
When you are executing T-SQL statement with sp_executesql you can pass parameters.
Related
Good afternoon,
I'm creating a trigger to be present on 3 databases, one of which is on a linkedserver.
When inserting data in one of the databases, the objective is to replicate the information in the remaining databases.
Whenever I do an insert, I get the error 'Transaction context in use by another session.'
Anyone knows why this is happening? And is there a way to surprass this?
DROP TRIGGER Insertdata
USE A
GO
SET ANSI_NULLS ON
SET ANSI_WARNINGS ON
SET QUOTED_IDENTIFIER OFF
GO
CREATE TRIGGER Insertdata ON DATA WITH ENCRYPTION FOR INSERT AS
SET NOCOUNT ON
if(Upper((SELECT DB_NAME()))=UPPER('A')) Begin
----------------------------------------------------------------------------------------------
--------------------------------------------- Insert Data ------------------------------------
----------------------------------------------------------------------------------------------
--Create variable for database name and query variable
DECLARE #DB_Name VARCHAR(100) -- database name
DECLARE #Local VARCHAR(1) -- database name
DECLARE #query VARCHAR(4000) -- query variable
--Declare the cursor
DECLARE db_cursor CURSOR LOCAL FOR
-- Populate the cursor with the selected database name
select t.name,t.islocal from (
Select name, '1' as islocal from master.sys.databases WHERE name in ('A','B','C')
union all
select name, '0' as islocal from LinkedServer.master.sys.databases WHERE name in ('A','B','C')
)t
OPEN db_cursor
--Moves the cursor to the first point and put that in variable name
FETCH NEXT FROM db_cursor INTO #DB_Name,#Local;
-- while loop to go through all the DB selected
WHILE ##FETCH_STATUS = 0
BEGIN
if(#Local ='0') begin
Set #DB_Name = 'LinkedServer.' + #DB_Name
end
-- Check if received data exists
SET #query = N'Select id from ' + #DB_Name +'.dbo.data where data.id='''+(Select inserted.id from inserted)+''''
EXEC(#query)
--If doesnt exists, then insert data
if ##ROWCOUNT < 1 begin
SET #query = N'INSERT INTO ' + #DB_Name +'.dbo.data(id,name,surname)
values('+''''+(select inserted.id from inserted)+''''
+','+''''+(select convert(varchar,inserted.name) from inserted)+''''
+','+''''+(select convert(varchar,inserted.surname) from inserted)+''''
+')'
EXEC (#query)
--Fetch the next record from the cursor
end
FETCH NEXT FROM db_cursor INTO #DB_Name,#Local
END
--Close and deallocate cursor
CLOSE db_cursor
DEALLOCATE db_cursor
End
GO
SET ANSI_NULLS OFF
SET ANSI_WARNINGS OFF
SET QUOTED_IDENTIFIER OFF
GO
EDIT:
I think the problem here is the fact, that my trigger is being executed on different databases. For example, the current database is A, so the trigger should execute on B and C, but I realized that when A calls B, B executes the trigger aswell. C doesn't executes because gives the error on B. Thought db_name() would fix this, but guess it doesn't
I'm trying to make a dynamic query, using a cursor, I wanna create filegroups to every tables on my database, I have this:
DECLARE #name VARCHAR(50)
DECLARE #query VARCHAR(50)
DECLARE vend_cursor CURSOR
FOR SELECT name FROM sys.tables order by name asc
OPEN vend_cursor
FETCH NEXT FROM vend_cursor;
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'FG_'+#name
FETCH NEXT FROM vend_cursor INTO #name;
END
CLOSE vend_cursor
DEALLOCATE vend_cursor
The print is because then I can see how the filegroup name will be, but I wanna add this:
ALTER DATABASE AdventureWorks2012
ADD FILEGROUP FG_filegroupname
I know I have to use 'exec sys.sp_executesql', but how can I add this to my query? thanks in advance
Yes you can do this with sp_executeSQL but the most important thing is that you need to setup a GLOBAL cursor, because sp_executeSQL is not in the same scope as the procedure which you are executing. See example
DECLARE #SQL nvarchar(1024),
#name varchar(255);
SET #SQL = 'DECLARE vend_cursor CURSOR GLOBAL
FOR
SELECT name FROM sys.tables order by name asc';
EXECUTE sp_executesql #SQL;
OPEN vend_cursor
FETCH NEXT FROM vend_cursor INTO #name;
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'FG_'+#name
FETCH NEXT FROM vend_cursor INTO #name;
END
CLOSE vend_cursor
DEALLOCATE vend_cursor
All,
Trying to set a cursor on a table value inside a table variable, but it does not work. can anyone comment on how I can fix this?
** the code below is called from another stored procedure which provides the value for the tablename variable **
ALTER PROCEDURE [dbo].[usrSetLTDNormDist]
-- Add the parameters for the stored procedure here
#TableName Sysname,
---...
DECLARE #SQLCommand1 NVARCHAR(MAX) = N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName'
EXECUTE dbo.sp_executesql #sqlCommand1
-- Open Cursor
Open #RecCursor1
Fetch Next From #RecCursor1
Into #Volume, #TransDate
---...
Add PRINT #SQLCommand1 between the DECLARE and EXECUTE statements to review what is actually being executed. Based on your code snippet, you will see
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName
...that is, the value you set in #TableName is not automagically added to the script. Here's the way I write these things:
DECLARE #SQLCommand1 NVARCHAR(MAX)
SET #SQLCommand1 = replace(N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from <#TableName>'
,'<#TableName>', #TableName)
PRINT #SQLCommand1
EXECUTE dbo.sp_executesql #sqlCommand1
I use the < > characters to make the replaced values stand out.
This script demonstrates the general technique:
create table T (ID int not null)
go
insert into T(ID) values (99)
go
declare #TableName sysname
declare #ID int
set #TableName = 'T'
declare #SQL nvarchar(max) = N'declare boris cursor for select ID from ' +
QUOTENAME(#TableName)
exec sp_executesql #SQL
open boris
fetch next from boris into #ID
while ##FETCH_STATUS = 0
begin
print #ID
fetch next from boris into #ID
end
close boris
deallocate boris
Producing this output:
(1 row(s) affected)
99
However, I will offer my usual caution - if you're in a situation where you want to operate against multiple tables in the same way, this is usually a sign of a broken data model. Usually there ought to be a single table with additional columns containing data that serves to differentiate the values.
I'm using SQL Server 2005/2008, I have a stored procedure that doesn't use string concatenation for generating an EXEC statement, but it does use a dynamic name for the stored procedure.
I think the #stored_procedure_name and potentially the #object_name parameter are both vulnerable. However all the Dynamic SQL links I read assume that you are concatenating your SQL statement inside a string - so I'm wondering if it might actually be OK.
Note, just for posting this, I've made the code generic by calling the table objects - so it might not necessarily make logical sense.
Here is the code:
CREATE PROCEDURE [dbo].[my_dodgy_sp]
#object_name varchar(50) = 'All'
AS
BEGIN
DECLARE #stored_procedure_name varchar(100);
DECLARE object_cursor CURSOR FOR
SELECT stored_procedure_name
FROM [dbo].[objects]
WHERE [stored_procedure_name] <> ''
AND ([name] = #object_name)
OPEN object_cursor
FETCH NEXT FROM object_cursor
INTO #stored_procedure_name
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC #stored_procedure_name #object_id OUTPUT;
FETCH NEXT FROM object_cursor
INTO #stored_procedure_name
END
CLOSE object_cursor;
DEALLOCATE object_cursor;
END
Try this:
CREATE PROCEDURE [dbo].[my_dodgy_sp]
#object_name varchar(50) = 'All'
AS
BEGIN
DECLARE #stored_procedure_name sysname, #oID INT, #sql NVARCHAR(MAX), #object_id int;
DECLARE object_cursor CURSOR FOR
SELECT object_id, name
FROM sys.procedures
WHERE [name] = #object_name
OPEN object_cursor
FETCH NEXT FROM object_cursor
INTO #oID, #stored_procedure_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = N'EXEC ['+OBJECT_SCHEMA_NAME(#oid)+N'].['+#stored_procedure_name+N'] #Object_id OUTPUT'
EXEC sp_executesql #sql, N'#object_id int OUTPUT', #ObjectId =#object_id OUTPUT
PRINT #object_id -- we need to do smth with it?
FETCH NEXT FROM object_cursor
INTO #oID, #stored_procedure_name
END
CLOSE object_cursor;
DEALLOCATE object_cursor;
END
BUT
instead of using such a complicated way, may be you just call the parametrized procedure?
I have created a SQL Server 2005 stored procedure which finds dependent objects on a particular table.
I want to run this stored procedure for different database and for different tables. I have created cursor for this.
When I write USE #dbname, it tries to find the stored procedure in a #dbname and not the current database.
Can anybody please help me with how do I write this command in a cursor?
DECLARE name_cur CURSOR FOR
SELECT db_name, obj_name from Stats_Usage
WHERE last_user_update > '2011-06-01' ORDER BY db_name
DECLARE #tableName NVARCHAR (800)
DECLARE #dbName NVARCHAR(800)
DECLARE #sql NVARCHAR(900)
OPEN name_cur
FETCH name_cur INTO #dbName, #tableName
WHILE ##Fetch_Status = 0
BEGIN
SET #sql = 'USE '+#dbName +' EXEC proc_depend ' + #tableName
EXEC (#sql)
FETCH name_cur INTO #dbName, #tableName
END
CLOSE name_cur
DEALLOCATE name_cur
GO
You can fully qualify your Stored Procedure name.
Assuming the database your SP resides in is called procs (for example), you could amend your query to use the following:
SET #sql = 'USE '+#dbName +' EXEC procs.dbo.proc_depend ' + #tableName
EXEC (#sql)
Refactor your stored proc to check for dependant objects cross database. You'll want to send it a command like this:
exec proc_depend 'MyDatabase.dbo.MyTable';
Try this instead:
SET #sql = ' EXEC proc_depend ''' #dbName + '.dbo.'+ #tableName + ''';
You'll need to dig into & modify proc_depend to ensure that it can take a fully qualified object name like database.schema.table