execute stored procedure in remote server with variable names - sql

I have a cursor and its fetching database name and linked server name to execute a procedure in each of them
Declare getDBLinkServer cursor for
select DatabaseName, LinkServerName from DBnLinkServer
Open getDBLinkServer
fetch next from getDBLinkServer into #DatabaseName, #LinkServerName
While ##FETCH_STATUS = 0
Begin
exec #LinkServerName.#DatabaseName.dbo.someProcedure 'some'
fetch next from getDBLinkServer into #DatabaseName, #LinkServerName
End
Close getDBLinkServer
Deallocate getDBLinkServer
This is showing Incorrect syntax near '.' How can I avoid it?

Use dynamic sql. Like this:
declare #sql nvarchar(max)
set #sql = 'exec ' + #LinkServerName + '.' + #DatabaseName + '.dbo.'+#someProcedure+' ''some'''
exec sp_executeSQL #sql

Related

SQL Server 2014 - sp_describe_cursor_column for dynamic queries issue

Basically I want to know columns/aliases of result set from dynamic query. I tried to use sp_describe_cursor_column but without success.
It returns me that cursor does not exits. But I can fetch values from such cursor...
The code is :
ALTER PROC TestProc
AS
DECLARE #dynamicSQL nvarchar(200)
-- Have code that will construct the dynamic SQL
SET #dynamicSQL = ' select table_name, TABLE_TYPE from INFORMATION_SCHEMA.TABLES'
-- The cursor that will be filled by the dynamic SQL
DECLARE #outputCursor CURSOR
-- Create the dynamic SQL to fill a CURSOR instead
SET #dynamicSQL = 'SET #outputCursor = CURSOR FORWARD_ONLY STATIC FOR ' +
#dynamicSQL + ' ; OPEN #outputCursor'
-- Execute dynamic sql
exec sp_executesql -- sp_executesql will essentially create a sproc
#dynamicSQL, -- The SQL statement to execute (body of sproc)
N'#outputCursor CURSOR OUTPUT', -- The parameter list for the sproc: OUTPUT CURSOR
#outputCursor OUTPUT -- The parameter to pass to the sproc: the CURSOR
declare #Report cursor
exEC sp_describe_cursor_columns
#cursor_return = #Report OUTPUT
,#cursor_source = N'local'
,#cursor_identity = N'outputCursor';
-- Code that will just output the values from the cursor
DECLARE #tableName nvarchar(200), #table_type nvarchar(200);
FETCH NEXT FROM #outputCursor INTO #tableName, #table_type
-- Loop while there're more things in the cursor
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #tableName
FETCH NEXT FROM #outputCursor INTO #tableName, #table_type
END
-- Be nice, close & deallocate cursor
CLOSE #outputCursor
DEALLOCATE #outputCursor
And this is the result:
Msg 16916, Level 16, State 4, Procedure sp_describe_cursor_columns,
Line 23 A cursor with the name 'outputCursor' does not exist.
DATABASE_UPDATE
SYSTEM_CONFIGURATION ....
I want as result to see table_name , table_type.
Don't tell me that I can just extarct it from string, becasue user may send select * from xxxx.
I found some other way how to extract description of result set for dynamic queries.
declare #Table nvarchar(200) ;
DECLARE #dynamicSQL nvarchar(200)
SET #dynamicSQL = 'select table_name, TABLE_TYPE from INFORMATION_SCHEMA.TABLES'
SELECT #Table = COALESCE(#Table + ', ', '') + Name
FROM sys.dm_exec_describe_first_result_set
(#dynamicSQL, NULL,1)
print #Table

cursors within cursors for Auto_Fix users

I have the below script and I want to be able to run this against a dynamic list of databases except the system databases. That's straight forward enough. The tricky bit is each database could have a different list of users to run the fix command against. Would this be a 3rd cursor? My attempt below which is not properly populating the users for each database. Any help would be greatly appreciated.
SET nocount ON
GO
SET QUOTED_IDENTIFIER OFF
GO
--
-- Declare and define variables
--
DECLARE #databasename VARCHAR(50) -- database name
DECLARE #sqlcommand nvarchar(256) -- SQL Command generated
-- Include the in-scope database names into #name
DECLARE db_cursor CURSOR FOR
SELECT NAME
FROM master.dbo.sysdatabases
WHERE NAME NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'DBATools' ) -- don't include the databases
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Fixing Logins for '
+ Cast(#databasename AS VARCHAR)
DECLARE curSQL CURSOR FOR
SELECT "USE " + ( #databasename ) + ";" + " exec sp_change_users_login 'AUTO_FIX','" + NAME + "'"
SELECT NAME
FROM sys.sysusers
WHERE issqluser = 1
AND NAME NOT IN ( 'dbo', 'guest', 'INFORMATION_SCHEMA', 'sys' )
OPEN curSQL
FETCH curSQL INTO #sqlcommand
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #sqlcommand
EXEC (#sqlcommand)
FETCH curSQL INTO #sqlcommand
END
CLOSE curSQL
DEALLOCATE curSQL
FETCH NEXT FROM db_cursor INTO #databasename
END
CLOSE db_cursor
DEALLOCATE db_cursor
Or you can do it cursorless.
DECLARE #Output NVARCHAR(MAX)
SET #Output = ''
SELECT #Output = #Output +
'--Fixing Logins For ' + name + CHAR(10) +
'USE ' + name + CHAR(10) +
'EXEC sp_change_users_login ''AUTO_FIX'','''+
(
SELECT b.name
FROM sys.sysusers as b
WHERE issqluser = 1
AND b.name NOT IN ( 'dbo', 'guest', 'INFORMATION_SCHEMA', 'sys' )
)
+''''+CHAR(10)+CHAR(10)
FROM master.dbo.sysdatabases
WHERE NAME NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'DBATools' )
PRINT #Output
The main thing with your procedure is that, unlike PHP, TSQL cannot use single and double quotes interchangeably. So I fixed up your dynamic SQL string.
The other minor thing appears to have been a disjointed query from the cursor declaration - in yours, you follow the second declare with a select clause, without an accompanying from clause, which would have thrown on "Name" not being able to be found for that cursor. So I fixed that up, too.
DECLARE #databasename SYSNAME, #sqlcommand nvarchar(max)
DECLARE db_cursor CURSOR FOR
SELECT NAME
FROM master.dbo.sysdatabases
WHERE NAME NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'DBATools' ) -- don't include the databases
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Fixing Logins for '
+ Cast(#databasename AS VARCHAR)
DECLARE curSQL CURSOR FOR
SELECT 'USE ' + #databasename + ';' + ' exec sp_change_users_login ''AUTO_FIX'',''' + NAME + ''';'
FROM sys.database_principals
WHERE Type='S'
AND NAME NOT IN ( 'dbo', 'guest', 'INFORMATION_SCHEMA', 'sys' )
OPEN curSQL
FETCH curSQL INTO #sqlcommand
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #sqlcommand
--EXEC (#sqlcommand)
FETCH curSQL INTO #sqlcommand
END
CLOSE curSQL
DEALLOCATE curSQL
FETCH NEXT FROM db_cursor INTO #databasename
END
CLOSE db_cursor
DEALLOCATE db_cursor
EDIT: Switched table to database_principals rather than sys.sysusers per comment.

Multiple values in a cursor which executes a stored procedure

I have a cursor which executes a stored procedure. I have introduced a new variable dbname into the cursor and I get an exception error near dbname. This change was introduced so that the stored procedure storedProc_getOutputsByRuncan be executed on different databases.
#
exec( '
DECLARE #dbName nvarchar(100)
DECLARE #runID INT
DECLARE #getRunDetails CURSOR
DECLARE #delayLoad bigint
SET #delayLoad = 1
SET #getRunDetails = CURSOR FOR
SELECT DBName, RunID from ' + #temp_table_runID + '
OPEN #getRunDetails
FETCH NEXT
FROM #getRunDetails INTO #dbName, #runID
WHILE ##FETCH_STATUS = 0
BEGIN
-- I have tried at this point printing #runid and #dbname and it prints fine.Error in line below
INSERT INTO ' + #temp_table_outputs + ' Execute ''#dbname''.dbo.storedProc_getOutputsByRun
#runID, #delayLoad
FETCH NEXT
FROM #getRunDetails INTO #dbName, #runID
END
CLOSE #getRunDetails
DEALLOCATE #getRunDetails')
If you were to wrap all this up in its own variable, and print it, this line
'INSERT INTO ' + #temp_table_outputs + ' Execute ''#dbname''.dbo.storedProc_getOutputsByRun'
Would yield something like this:
INSERT INTO myTable Execute '#dbname'.dbo.storedProc_getOutputsByRun
The problem being you're not escaping the string correctly to replace #dbname with an actual value.
The fix would be to nest another string within the main string, and execute that.
Something like this INSIDE the main string:
declare #sql nvarchar(max)
set #sql = ''INSERT INTO ' + #temp_table_outputs + ' Execute ''+#dbname+''.dbo.storedProc_getOutputsByRun ''+cast(#runID as varchar)+'', ''+cast(#delayLoad as varchar)+''''
print #sql
exec(#sql)
Which would yield a final product similar to this...
exec( '
DECLARE #dbName nvarchar(100)
DECLARE #runID INT
DECLARE #getRunDetails CURSOR
DECLARE #delayLoad bigint
SET #delayLoad = 1
SET #getRunDetails = CURSOR FOR
SELECT DBName, RunID from ' + #temp_table_runID + '
OPEN #getRunDetails
FETCH NEXT
FROM #getRunDetails INTO #dbName, #runID
WHILE ##FETCH_STATUS = 0
BEGIN
declare #sql nvarchar(max)
set #sql = ''INSERT INTO ' + #temp_table_outputs + ' Execute ''+#dbname+''.dbo.storedProc_getOutputsByRun ''+cast(#runID as varchar)+'', ''+cast(#delayLoad as varchar)+''''
print #sql
--exec(#sql)
FETCH NEXT
FROM #getRunDetails INTO #dbName, #runID
END
CLOSE #getRunDetails
DEALLOCATE #getRunDetails')
You may need to tweak this a bit, but this is easy now, just comment out the EXEC(#SQL) as I have done inside the new code, and print it out to screen. Copy the messages to a new SSMS window, and check the syntax.

How to do an Alter in SQL Cursor

I have 100's of databases for which I need to do an Alter to a procedure, they all have the same procedure. How can I add a cursor that will allow me to do this alter?.
DECLARE #databasename varchar(100)
DECLARE #Command nvarchar(200)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = 'ALTER PROCEDURE ''' + #databasename + '''.[dbo].[DeleteAccountUpgrade]
#Id INT
AS
DELETE FROM AccountUpgrades WHERE Id = #Id'
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #databasename
END
CLOSE database_cursor
DEALLOCATE database_cursor
It says that i need to declare #Id but i dont understand why, I just want to run that alter on each database, i am not sending any data to execute the procedure, i just want to modify the existing procedure on all databases.
Use nest sp_executesql calls. This allow you to execute DDL against other databases.
DECLARE #databasename varchar(100)
DECLARE #Command nvarchar(200)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = N'USE ' + QUOTENAME(#databasename) +
' EXEC sp_executesql N''ALTER PROCEDURE [dbo].[DeleteAccountUpgrade] ' +
'#Id INT ' +
'AS ' +
'DELETE FROM AccountUpgrades WHERE Id = #Id'''
--PRINT #Command
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #databasename
END
CLOSE database_cursor
DEALLOCATE database_cursor
You need to use EXEC by itself, and not with sp_executesql.
EXEC sp_executesql #Command
should be changed to:
EXEC(#Command)
The sp_executesql procedure does parameterization with variables. This is where the error is coming from. See also https://stackoverflow.com/a/16308447/1822514

How to drop all stored procedures at once in SQL Server database?

Currently we use separate a drop statements for each stored procedure in the script file:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MySP]')
AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[MySP]
Is there a way to drop them all at once, or maybe in a loop?
I would prefer to do it this way:
first generate the list of stored procedures to drop by inspecting the system catalog view:
SELECT 'DROP PROCEDURE [' + SCHEMA_NAME(p.schema_id) + '].[' + p.NAME + '];'
FROM sys.procedures p
This generates a list of DROP PROCEDURE statements in your SSMS output window.
copy that list into a new query window, and possibly adapt it / change it and then execute it
No messy and slow cursors, gives you the ability to check and double-check your list of procedure to be dropped before you actually drop it
Something like (Found at Delete All Procedures from a database using a Stored procedure in SQL Server).
Just so by the way, this seems like a VERY dangerous thing to do, just a thought...
declare #procName varchar(500)
declare cur cursor
for select [name] from sys.objects where type = 'p'
open cur
fetch next from cur into #procName
while ##fetch_status = 0
begin
exec('drop procedure [' + #procName + ']')
fetch next from cur into #procName
end
close cur
deallocate cur
In the Object Explorer pane, select the Stored Procedures folder.
Press F7 (or from the main menu, choose View > Object Explorer Details).
Select all procedures except the System Table.
Press Delete button and select OK.
You can delete Tables or Views in the same manner.
create below stored procedure in your db(from which db u want to delete sp's)
then right click on that procedure - click on Execute Stored Procedure..
then click ok.
create Procedure [dbo].[DeleteAllProcedures]
As
declare #schemaName varchar(500)
declare #procName varchar(500)
declare cur cursor
for select s.Name, p.Name from sys.procedures p
INNER JOIN sys.schemas s ON p.schema_id = s.schema_id
WHERE p.type = 'P' and is_ms_shipped = 0 and p.name not like 'sp[_]%diagram%'
ORDER BY s.Name, p.Name
open cur
fetch next from cur into #schemaName,#procName
while ##fetch_status = 0
begin
if #procName <> 'DeleteAllProcedures'
exec('drop procedure ' + #schemaName + '.' + #procName)
fetch next from cur into #schemaName,#procName
end
close cur
deallocate cur
I think this is the simplest way:
DECLARE #sql VARCHAR(MAX)='';
SELECT #sql=#sql+'drop procedure ['+name +'];' FROM sys.objects
WHERE type = 'p' AND is_ms_shipped = 0
exec(#sql);
To get drop statements for all stored procedures in a database
SELECT 'DROP PROCEDURE' + ' '
+ F.NAME + ';'
FROM SYS.objects AS F where type='P'
DECLARE #sql VARCHAR(MAX)
SET #sql=''
SELECT #sql=#sql+'drop procedure ['+name +'];' FROM sys.objects
WHERE type = 'p' AND is_ms_shipped = 0
exec(#sql);
Try this, it work for me
DECLARE #spname sysname;
DECLARE SPCursor CURSOR FOR
SELECT SCHEMA_NAME(schema_id) + '.' + name
FROM sys.objects
WHERE type = 'P';
OPEN SPCursor;
FETCH NEXT FROM SPCursor INTO #spname;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC('DROP PROCEDURE ' + #spname);
FETCH NEXT FROM SPCursor INTO #spname;
END
CLOSE SPCursor;
DEALLOCATE SPCursor;
DECLARE #DeleteProcCommand NVARCHAR(500)
DECLARE Syntax_Cursor CURSOR
FOR
SELECT 'DROP PROCEDURE ' + p.NAME
FROM sys.procedures p
OPEN Syntax_Cursor
FETCH NEXT FROM Syntax_Cursor
INTO #DeleteProcCommand
WHILE (##FETCH_STATUS = 0)
BEGIN
EXEC (#DeleteProcCommand)
FETCH NEXT FROM Syntax_Cursor
INTO #DeleteProcCommand
END
CLOSE Syntax_Cursor
DEALLOCATE Syntax_Cursor
Try this:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += N'DROP PROCEDURE dbo.'
+ QUOTENAME(name) + ';
' FROM sys.procedures
WHERE name LIKE N'spname%'
AND SCHEMA_NAME(schema_id) = N'dbo';
EXEC sp_executesql #sql;
ANSI compliant, without cursor
DECLARE #SQL national character varying(MAX)
SET #SQL= ''
SELECT #SQL= #SQL+ N'DROP PROCEDURE "' + REPLACE(SPECIFIC_SCHEMA, N'"', N'""') + N'"."' + REPLACE(SPECIFIC_NAME, N'"', N'""') + N'"; '
FROM INFORMATION_SCHEMA.ROUTINES
WHERE (1=1)
AND ROUTINE_TYPE = 'PROCEDURE'
AND ROUTINE_NAME NOT IN
(
'dt_adduserobject'
,'dt_droppropertiesbyid'
,'dt_dropuserobjectbyid'
,'dt_generateansiname'
,'dt_getobjwithprop'
,'dt_getobjwithprop_u'
,'dt_getpropertiesbyid'
,'dt_getpropertiesbyid_u'
,'dt_setpropertybyid'
,'dt_setpropertybyid_u'
,'dt_verstamp006'
,'dt_verstamp007'
,'sp_helpdiagrams'
,'sp_creatediagram'
,'sp_alterdiagram'
,'sp_renamediagram'
,'sp_dropdiagram'
,'sp_helpdiagramdefinition'
,'fn_diagramobjects'
,'sp_upgraddiagrams'
)
ORDER BY SPECIFIC_NAME
-- PRINT #SQL
EXEC(#SQL)
Without cursor, non-ansi compliant:
DECLARE #sql NVARCHAR(MAX) = N''
, #lineFeed NVARCHAR(2) = CHAR(13) + CHAR(10) ;
SELECT #sql = #sql + N'DROP PROCEDURE ' + QUOTENAME(SPECIFIC_SCHEMA) + N'.' + QUOTENAME(SPECIFIC_NAME) + N';' + #lineFeed
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE'
-- AND SPECIFIC_NAME LIKE 'sp[_]RPT[_]%'
AND ROUTINE_NAME NOT IN
(
SELECT name FROM sys.procedures WHERE is_ms_shipped <> 0
)
ORDER BY SPECIFIC_NAME
-- PRINT #sql
EXECUTE(#sql)
By mixing the cursor and system procedure, we would have a optimized solution, as follow:
DECLARE DelAllProcedures CURSOR
FOR
SELECT name AS procedure_name
FROM sys.procedures;
OPEN DelAllProcedures
DECLARE #ProcName VARCHAR(100)
FETCH NEXT
FROM DelAllProcedures
INTO #ProcName
WHILE ##FETCH_STATUS!=-1
BEGIN
DECLARE #command VARCHAR(100)
SET #command=''
SET #command=#command+'DROP PROCEDURE '+#ProcName
--DROP PROCEDURE #ProcName
EXECUTE (#command)
FETCH NEXT
FROM DelAllProcedures
INTO #ProcName
END
CLOSE DelAllProcedures
DEALLOCATE DelAllProcedures
ANSI compliant, without cursor
PRINT ('1.a. Delete stored procedures ' + CONVERT( VARCHAR(19), GETDATE(), 121));
GO
DECLARE #procedure NVARCHAR(max)
DECLARE #n CHAR(1)
SET #n = CHAR(10)
SELECT #procedure = isnull( #procedure + #n, '' ) +
'DROP PROCEDURE [' + schema_name(schema_id) + '].[' + name + ']'
FROM sys.procedures
EXEC sp_executesql #procedure
PRINT ('1.b. Stored procedures deleted ' + CONVERT( VARCHAR(19), GETDATE(), 121));
GO
Try this:
declare #procName varchar(500)
declare cur cursor
for SELECT 'DROP PROCEDURE [' + SCHEMA_NAME(p.schema_id) + '].[' + p.NAME + ']'
FROM sys.procedures p
open cur
fetch next from cur into #procName
while ##fetch_status = 0
begin
exec( #procName )
fetch next from cur into #procName
end
close cur
deallocate cur