Cursor with Dynamic SQL Query - sql

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

Related

Issue executing Dynamic sql query

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.

Nested loop through databases in loop

I have 4 the same databases
('User1','User2','User3','User4')
And a loop doing what it supposed to do:
DECLARE #json NVARCHAR(2000)
DECLARE #getid CURSOR
SET #getid = CURSOR FOR
SELECT [custom_data] FROM [User1].[usr].[table] where [custom_data] like '%:true%' or [custom_data] like '%:false%'
OPEN #getid
FETCH NEXT
FROM #getid INTO #json
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT JSON_MODIFY(#json, '$.field','value')
FETCH NEXT FROM #getid INTO #json
END
CLOSE #getid
DEALLOCATE #getid
My question is: How do I iterate through databases? I have tried to nest the loop containing variable with database name and then execute query in this way:
First I:
DECLARE C CURSOR FOR SELECT name FROM sysdatabases WHERE name IN ('User1','User2','User3','User4')
as described in many examples and after that I wanted to concat db name into query and execue it like that:
DECLARE #dbname varchar(50)
DECLARE C CURSOR FOR SELECT name FROM sysdatabases
WHERE name IN ('User1','User2','User3','User4')
OPEN C
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC('DECLARE #json NVARCHAR(2000);
DECLARE #getid CURSOR; SET #getid = CURSOR FOR SELECT [custom_data] FROM '+#dbName+'.[usr].[transactions]
OPEN #getid;
FETCH NEXT;
FROM #getid INTO #json;
WHILE ##FETCH_STATUS = 0;
BEGIN;
SELECT JSON_MODIFY(#json, ''$.Field'',''Value'');
FETCH NEXT FROM #getid INTO #json
END
CLOSE #getid
DEALLOCATE #getid')
FETCH NEXT FROM C INTO #dbname
END
CLOSE C
DEALLOCATE C
but it finishes loop with no errors with massage
Commands completed successfully.
but it changes nothing. I would appreciate your assistance
Before you can execute a statement within the context of a specific database in ad-hoc sql your have to execute a USE statement like this (don't omit the GO instruction otherwise it won't work):
SET #sql = 'USE '+#dbname+';'+CHAR(13)+CHAR(10)+'GO'+CHAR(13)+CHAR(10)+'SELECT DB_NAME();'
Also, don't forget to use a FETCH NEXT FROM C INTO #dbname before your start your WHILE too otherwise your #dbname won't contain a usable value.

Query that will apply to EVERY SQL Server database

I'm testing a drop database query for all databases that begin with a specific prefix. However, because that can easily lead to horrific things I'm taking my drop database query
declare #dbname nvarchar (200);
declare #query nvarchar (max);
DECLARE db_cursor CURSOR FOR
select name from sys.databases
where name like 'PREFIX%'
Open db_cursor
fetch next from db_cursor into #dbname
while ##FETCH_STATUS = 0
BEGIN
set #query = 'Drop Database ['+ #dbname + ']'
Exec(#query)
FETCH NEXT FROM db_cursor INTO #dbname
END
Close db_cursor
deallocate db_cursor
and want to change the Drop Database part to something that is less scary.
So MAIN QUESTION is there a simple SQL query that I could put in there that would always apply to any SQL Server database? So I know that this query will only affect the databases I want it to before switching it back to Drop Database?
EDIT: Better yet, a query that will return the names of the databases.
Like select name from sys.databases but one that will work with ['+ #dbname + '] to return only the names of databases with that prefix to ensure that this query affects the appropriate databases.
Instead of Exec(#query), just call PRINT #query. That will show you the SQL that you intend to run.
PRINT Documentation
First of all just execute the following query and it will tell you what databases will it bring forward in the rest of the code
select name from sys.databases
where name like 'PREFIX%'
Finally add a PRINT statement to see the final DROP DATABASE statements dynamically build inside the cursor.
Some minor improvement in your code:
declare #dbname SYSNAME;
declare #query nvarchar (max);
DECLARE db_cursor CURSOR LOCAL FORWARD_ONLY FOR
select name from sys.databases
where name like 'PREFIX%'
Open db_cursor
fetch next from db_cursor into #dbname
while (##FETCH_STATUS = 0)
BEGIN
set #query = 'Drop Database '+ QUOTENAME(#dbname)
--Exec sp_executesql #query --<-- For execution
-- PRINT #query --<-- For debugging
FETCH NEXT FROM db_cursor INTO #dbname
END
Close db_cursor
deallocate db_cursor

Stored procedure without string concatenation SQL Injection vulnerabilities

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?

Run script on multiple DBs (SQL Server)?

Let's say I have some update script:
update sometable set somecolumn = 'somevalue' where xyz = 0
Now let's say I have multiple databases, like DB1, DB2, DB3 and so on. How could I run this script on all of them without doing it manually?
Thanks :)
You can do this using cursor
get list of all server in your lan or in network
create cursor for that
Than make use of sp_executesql to run you update script with forpart query
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [sp_cross_db_query]
#SQLQuery varchar(400)
AS
DECLARE #DB_Name varchar(100)
DECLARE database_cursor CURSOR FOR
SELECT DatabaseName
FROM Management.dbo.Customers
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
exec(
'USE [' + #DB_Name + '];' +
#SQLQuery
)
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
to run the query
exec sp_cross_db_query 'SELECT count(*) FROM Products'
If you wanted all databases, you can use sp_MSforeachdb:
http://www.databasejournal.com/features/mssql/article.php/3441031/SQL-Server-Undocumented-Stored-Procedures-spMSforeachtable-and-spMSforeachdb.htm
EXEC sp_MSforeachdb #command1="UPDATE ?..sometable SET somecolumn='somevalue' WHERE xyz=0"
Or for specific databases, you could try some of the logic as seen here:
http://www.sqlservercurry.com/2009/04/6-common-uses-of-undocumented-stored.html
Hope that helps.