sp_OAMethod 'DeleteFile' Method not working in SQL Server 2008 R2 - sql

I have an sql stored procedure that I use to delete files from the windows file system using the sp_OAMethod. This used to work fine when we were using sql server 2005, however, it does not work at all now when using sql server 2008 R2. I have read that you can use SQLDMO/SQLCLR however, I cannot find any decent information regarding these methods. My previous code is below:
-- declare variables
declare #ObjectID nvarchar(10),
#ObjectType nvarchar(255),
#BackupName nvarchar(255),
#BackupLocation nvarchar(255),
#ExpiryDate datetime,
#DeletedStatus bit,
#SQL nvarchar(4000),
#SQL1 nvarchar(4000),
#SQL2 nvarchar(4000),
#Result int,
#FSO_Token int,
#FileLocation nvarchar(4000)
-- declare cursor for table backups
declare backupexpired_cursor cursor for
select dbo.tbl_BackupObjects.ObjectID, dbo.tbl_BackupObjects.ObjectType, dbo.tbl_BackupObjects.BackupName,
dbo.tbl_BackupObjects.BackupLocation, dbo.tbl_BackupObjects.ExpiryDate, dbo.tbl_BackupObjects.Deleted
from dbo.tbl_BackupObjects
where dbo.tbl_BackupObjects.Deleted <> 1
-- open cursor
open backupexpired_cursor
-- fetch the next record from the cursor
fetch next from backupexpired_cursor into #ObjectID, #ObjectType, #BackupName, #BackupLocation, #ExpiryDate, #DeletedStatus
while (##FETCH_STATUS <> -1)
begin
if (##FETCH_STATUS <> -2)
begin
if (#ExpiryDate < GetDate())
begin
if (#ObjectType = 'Table')
begin
begin try
begin transaction
-- Only done if the object type is a table object
-- Remove old backup
select #SQL = 'drop table dbo.' + quotename(#BackupName)
exec sp_executesql #SQL
-- update the deleted status and the date deleted of the deleted object
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 1,
DeletedDate = GetDate()
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
commit transaction
end try
begin catch
rollback transaction
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 0,
DeletedDate = NULL
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
end catch
end
else
begin
begin try
begin transaction
-- Only done if the object(view, stored procedure, and/or function is saved
-- in a file located on the windows file system.
-- Create File Location
set #FileLocation = 'G:\Backup Registry Script Files\' + #BackupLocation + '\' + #BackupName + ''
-- Create a token of the object
EXEC #Result = sp_OACreate 'Scripting.FileSystemObject', #FSO_Token OUTPUT
-- Call the deletefile method using the #FileLocation parameter and the token created above:
-- - The object token created by sp_OACreate
-- - The method name
-- - The method's return value
-- - Parameters that will be used by the object method
EXEC #Result = sp_OAMethod #FSO_Token, 'DeleteFile', NULL, #FileLocation
-- Execute ole method
EXEC #Result = sp_OADestroy #FSO_Token
-- update the deleted status and the date deleted of the deleted object
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 1,
DeletedDate = GetDate()
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
commit transaction
end try
begin catch
rollback transaction
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 0,
DeletedDate = GetDate()
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
end catch
end
end
end
-- fetch the next record from the cursor
fetch next from backupexpired_cursor into #ObjectID, #ObjectType, #BackupName, #BackupLocation, #ExpiryDate, #DeletedStatus
end
-- set the Last and Next Removal Dates
select #SQL2 = 'update tbl_BackupRemovalDate
set LastRemovalDate = GetDate(),
NextRemovalDate = GetDate() + 7'
exec sp_executesql #SQL2
-- close cursor
close backupexpired_cursor
deallocate backupexpired_cursor
I have seen that SQLDMO is quite similar to what I have, however I cannot find any information on how to delete a file system file using this method. Can anyone help?

Do you have Enable Ole Automation Procedures feature ?
try this
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'Ole Automation Procedures', 1
RECONFIGURE

Related

Cursor within a cursor for database dependent permissions

It would seem that my code should work, however testing indicates that all the results from the inner cursor are based on the current database at execution of the script and not dependent upon the USE statement within the script.
WHAT DID I FORGET?
DECLARE #Debug BIT = 1
DECLARE #newgrp VARCHAR(100) = 'ChangeTrakingViewableRole'
DECLARE #obj VARCHAR(100)
DECLARE #tsql VARCHAR(900)
DECLARE #tsql2 VARCHAR(900)
DECLARE #msg VARCHAR(900)
DECLARE #SchName VARCHAR(55)
DECLARE #TblName sysname
IF #Debug = 'TRUE' PRINT 'Debuging ON'
IF COALESCE(#newgrp,'') = ''
BEGIN
PRINT 'There was no DatabaseRole, User or Group Specified to take the place of the Public Role'
SET NOEXEC ON
END
ELSE
BEGIN
DECLARE DbCursor CURSOR FOR
SELECT 'USE '+DB_NAME(database_id) FROM sys.change_tracking_databases
OPEN DbCursor
FETCH NEXT FROM DbCursor INTO #obj
WHILE ##Fetch_Status = 0
BEGIN
SET #tsql2 = #obj+'; '
RAISERROR (#tsql2, 0, 1) WITH NOWAIT
EXEC sp_sqlexec #tsql2
-----------Commands within this next section are all database dependent
BEGIN --GRANT [VIEW CHANGE TRACKING] TO Change Tracking Enabled Tables
IF NOT EXISTS (SELECT name FROM sys.database_principals where name = #newgrp)
BEGIN
SET #tsql = N'CREATE ROLE '+#newgrp+' AUTHORIZATION [dbo]'
IF #Debug = 'TRUE'
BEGIN
SET #Msg = #tsql
RAISERROR (#Msg, 0, 1) WITH NOWAIT
END
ELSE
BEGIN
EXEC sp_sqlexec #tsql
END
END
DECLARE TblCursor CURSOR FOR
SELECT sch.name, tbl.name
FROM sys.change_tracking_tables chg
JOIN sys.tables tbl ON chg.object_id=tbl.object_id
JOIN sys.schemas sch ON tbl.schema_id=sch.schema_id
ORDER BY sch.name, tbl.name
OPEN TblCursor
FETCH NEXT FROM TblCursor INTO #SchName,#TblName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #tsql = 'GRANT VIEW CHANGE TRACKING ON ['+#SchName+'].['+#TblName+'] TO '+#newgrp
IF #Debug = 'TRUE'
BEGIN
SET #Msg = #tsql
RAISERROR (#Msg, 0, 1) WITH NOWAIT
END
ELSE
BEGIN
EXEC sp_sqlexec #tsql
END
FETCH NEXT FROM TblCursor INTO #SchName,#TblName
END
CLOSE TblCursor
DEALLOCATE TblCursor
END
FETCH NEXT FROM DbCursor INTO #obj
END
CLOSE DbCursor
DEALLOCATE DbCursor
END
The USE statements in your outer cursor are not doing anything for the statements generated aftwerward because they're being executed independently. When your outer cursor executes the USE it is just valid for the scope of the first EXEC sp_sqlexec call; it doesn't change the database context of the overall script. The context under which the rest of your script runs is still that of the overall script, meaning those statements will get run in the current database every time.
Bascially, you need to change this to generate a single script with the entirety of what you want to execute within the dynamic db context top to bottom, with the USE at the top, and then execute that whole thing in a single call to EXEC or sp_executesql.

Dynamic Stored Procedure with View name and Date as parameters

I created a stored procedure which takes a view name and date as parameters and checks if there is record for that date in the view. However, I get the following error
'Operand type clash: date is incompatible with int'.
I am hoping that if the record exists that 1 will be returned else 0 will be returned and I can use that to make a decision in another stored procedure.
The code is listed below
CREATE PROCEDURE [dbo].[usr_RecordExist]
-- Add the parameters for the stored procedure here
#ViewName SYSNAME,
#TransDate Date
--<#Param2, sysname, #p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #DATEVARCHAR nvarchar(4000);
SET #DATEVARCHAR = CONVERT(NVARCHAR, #TransDate, 103);
-- Insert statements for procedure here
DECLARE #SQLCommand NVARCHAR(MAX) =
N'SELECT COUNT(SYMBOL) FROM ' + QUOTENAME(#ViewName) + 'WHERE TRANSDATE = ' + '' + #DATEVARCHAR + '';
EXECUTE [dbo].[sp_executesql]
#sqlCommand;
END
The expression + '' does nothing, use + '''' to add a single quote.
... + '''' + #DATEVARCHAR + '''';
You are using the right tools but in the wrong way, You should not concatenate parameters but pass them as parameters to the system stored procedure sp_executesql as shown below:
CREATE PROCEDURE [dbo].[usr_RecordExist]
#ViewName SYSNAME,
#TransDate Date
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLCommand NVARCHAR(MAX);
SET #SQLCommand = N'SELECT COUNT(SYMBOL) FROM ' + QUOTENAME(#ViewName)
+ N' WHERE TRANSDATE = #TransDate';
EXECUTE [dbo].[sp_executesql] #sqlCommand
,N'#TransDate Date'
,#TransDate
END
Edit
To get the count in an output parameter you would do the following:
CREATE PROCEDURE [dbo].[usr_RecordExist]
#ViewName SYSNAME,
#TransDate Date,
#Count INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLCommand NVARCHAR(MAX);
SET #SQLCommand = N'SELECT #Count = COUNT(SYMBOL) FROM ' + QUOTENAME(#ViewName)
+ N' WHERE TRANSDATE = #TransDate';
EXECUTE [dbo].[sp_executesql] #sqlCommand
,N'#TransDate Date, #Count INT OUTPUT'
,#TransDate
,#Count OUTPUT
END
Since you used QUOTENAME() for ViewName why not QUOTENAME(#DATEVARCHAR, '''') or QUOTENAME(#DATEVARCHAR, CHAR(39))
Cosmin got it. Although I also noticed you set #DATEVARCHAR to NVARCHAR(4000) even though convert(NVARCHAR without a length defaults to 30.

Using variables in Transact-sql exists subquery

this seems like it should be extraordinarily simple, so I apologize in advance if this information is easily accessible on the transact-sql documentation pages. I searched myself, but couldn't seem to find anything.
I'm trying to modify a transact-sql statement that currently runs on our Windows server 2000 box. I want to check if a table in another database exists, and then do a bunch of stuff. The database name is given as a string argument, '#dbName'
CREATE PROCEDURE CopyTables
#dbName char(4)
AS
IF EXISTS (SELECT * FROM #dbName.INFORMATION_SCHEMA.TABLES WHERE
TABLE_NAME = N'MainTable')
BEGIN
--Do Stuff
In it's current state, it doesn't like using the bare #dbName variable within the select statement. Is there special syntax for doing this?
Thanks in advance.
The below code should do what you want. As was mentioned previously, the account running the query would need the privilege to query the INFORMATION_SCHEMAs in the target database.
To future-proof your stored procedure, I'd also suggest increasing the length of the database name parameter and declaring it as an nchar or nvarchar in stead of char.
CREATE PROCEDURE CopyTables
#dbName char(4)
AS
DECLARE
#SQLStr nvarchar (max),
#Params nvarchar (max),
#Count tinyint;
SET
#Count = 0;
SET #SQLStr = N'SELECT #qCount = 1 FROM [' + #dbName + N'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N''MainTable''';
SET #Params = N'#qdbName char (4), #qCount tinyint OUTPUT';
EXECUTE sp_executesql #SQLStr, #Params, #qdbName = #dbName, #qCount = #Count OUTPUT;
IF #Count = 1
BEGIN
--Do Stuff
END; -- if
GO
Try doing the following:
DECLARE #dbName NVARCHAR(MAX) = 'master', #TableName NVARCHAR(MAX) = N'spt_monitor';
DECLARE #sql NVARCHAR(MAX) = N'SELECT * FROM [' + #dbName + N'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ''' + REPLACE(#TableName,N'''',N'''''') + N'''';
SET NOCOUNT OFF;
EXEC(#sql);
IF ##ROWCOUNT > 0 BEGIN;
-- DO STUFF
SELECT NULL;
END;
There are a few shortcomings to this solution:
1) It requires that the user executing the statement has SELECT access to the other database's INFORMATION_SCHEMA.TABLES
2) It has the side-effect of actually selecting the rows, so if you're using a reader to access the results, you'll have to call reader.NextResult() or await reader.NextResultAsync() because it actually outputs the results of the SELECT statement, rather than doing it in an IF EXISTS context.
By merging the two solutions, we get this:
DECLARE #dbName NVARCHAR(MAX) = 'master', #TableName NVARCHAR(MAX) = N'spt_monitor';
DECLARE #sql NVARCHAR(MAX) = N'SELECT #count = COUNT(*) FROM [' + #dbName + N'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ''' + REPLACE(#TableName,N'''',N'''''') + N'''';
DECLARE #Count INT;
EXECUTE sp_executesql #sql, N'#Count INT OUTPUT', #Count OUTPUT;
IF #Count > 0 BEGIN;
-- Do stuff
SELECT 'the table exists';
END ELSE BEGIN;
-- Do stuff
SELECT 'the table does not exist';
END;
This solution requires that the user executing the statement has SELECT access to the other database's INFORMATION_SCHEMA.TABLES, but it does not have the side-effect of selecting rows, like my previous solution.

Copy NAV Database Structure to another Database along with Data in SQL

I need to Copy a Company in NAV Database to another Company in Different Database with SQL Query. I have achived this using the code below, but if the Structure is different in the Source or Destination my code fails. Please help me in modifying it, so i can copy the Structure and Data from the Source and replace Data and Structure in the Destination.
The code is given below.
-- =============================================
-- Author: Prajeesh
-- Create date: 31.03.2015
-- Description: Function for copying comany from one database to another
-- =============================================
Create PROCEDURE [dbo].[sp_NAVCopyCompany_v2]
#sourcecompany varchar(max),
#targetdb varchar(max),
#targetcompany varchar(max)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
declare #tablename varchar(1000)
declare #columns varchar(max)
declare #columnname varchar (max)
declare #targettable varchar (max)
declare #isidentity int
declare #sqlcommand nvarchar (max) = 'select name from '+#targetdb+'.sys.all_objects where type=''U'' and object_id>0 and name like '''+#sourcecompany+'$%'''
declare #sqlcommandIdentity nvarchar (max)
declare #tablevar table(name varchar(300))
declare #columntablevar table(COLUMN_NAME varchar(300))
declare #identitytablevar table(C int)
insert into #tablevar(name) exec sp_executesql #sqlcommand
DECLARE table_cursor CURSOR for
select name from #tablevar
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #tablename
WHILE ##FETCH_STATUS = 0
BEGIN
--RAISERROR (#tablename, 0, 1) WITH NOWAIT
set #sqlcommand = 'SELECT COLUMN_NAME FROM '+#targetdb+'.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '''+#tablename+''' and COLUMN_NAME <> ''timestamp'''
DELETE from #columntablevar
insert into #columntablevar(COLUMN_NAME) exec sp_executesql #sqlcommand
DECLARE column_cursor CURSOR for select COLUMN_NAME from #columntablevar
select #columns=''
OPEN column_cursor
FETCH NEXT from column_cursor
INTO #columnname
WHILE ##FETCH_STATUS=0
BEGIN
SELECT #columns=#columns+',['+#columnname+']'
FETCH NEXT from column_cursor
INTO #columnname
END
CLOSE column_cursor;
DEALLOCATE column_cursor;
select #columns = SUBSTRING(#columns,2,LEN(#columns)-1)
--RAISERROR (#columns, 0, 1) WITH NOWAIT
select #targettable= #targetdb+'.dbo.['+#targetcompany+SUBSTRING(#tablename,LEN(#sourcecompany)+1,LEN(#tablename)-LEN(#sourcecompany)+1)+']'
--
select #isidentity=COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =#tablename AND COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1
--
set #sqlcommandIdentity = 'SELECT COUNT(*) as C FROM '+#targetdb+'.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME ='''+#tablename+''' AND COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, ''IsIdentity'') = 1'
DELETE from #identitytablevar
insert into #identitytablevar(C) exec sp_executesql #sqlcommandIdentity
select #isidentity=SUM(C) FROM #identitytablevar
RAISERROR (#targettable, 0, 1) WITH NOWAIT
set #sqlcommand = ''
IF (#isidentity>0)
set #sqlcommand = #sqlcommand + 'SET IDENTITY_INSERT '+#targettable+' ON;'
set #sqlcommand = #sqlcommand + 'delete from '+#targettable+';'
set #sqlcommand = #sqlcommand + 'insert into '+#targettable+ ' ('+ #columns + ')'
+ ' select '+#columns
+ ' from ['+#tablename+']'
IF (#isidentity>0)
set #sqlcommand = #sqlcommand + ';SET IDENTITY_INSERT '+#targettable+' OFF'
--RAISERROR (#sqlcommand, 0, 1) WITH NOWAIT
exec sp_executesql #sqlcommand
FETCH NEXT FROM table_cursor
INTO #tablename
END
CLOSE table_cursor;
DEALLOCATE table_cursor;
END
The Query is called as
use SourceDatabase
exec sp_NAVCopyCompany_v2 'Source Company_','Destination Database','Destination Company'

How to Check all stored procedure is ok in sql server?

How to check all stored procedure is ok in sql server if I drop a table or fields?
I found Cade's answer useful in formulating my own script for checking objects in a database, so I thought I'd share my script as well:
DECLARE #Name nvarchar(1000);
DECLARE #Sql nvarchar(1000);
DECLARE #Result int;
DECLARE ObjectCursor CURSOR FAST_FORWARD FOR
SELECT QUOTENAME(SCHEMA_NAME(o.schema_id)) + '.' + QUOTENAME(OBJECT_NAME(o.object_id))
FROM sys.objects o
WHERE type_desc IN (
'SQL_STORED_PROCEDURE',
'SQL_TRIGGER',
'SQL_SCALAR_FUNCTION',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'VIEW')
--include the following if you have schema bound objects since they are not supported
AND ISNULL(OBJECTPROPERTY(o.object_id, 'IsSchemaBound'), 0) = 0
;
OPEN ObjectCursor;
FETCH NEXT FROM ObjectCursor INTO #Name;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Sql = N'EXEC sp_refreshsqlmodule ''' + #Name + '''';
--PRINT #Sql;
BEGIN TRY
EXEC #Result = sp_executesql #Sql;
IF #Result <> 0 RAISERROR('Failed', 16, 1);
END TRY
BEGIN CATCH
PRINT 'The module ''' + #Name + ''' does not compile.';
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH
FETCH NEXT FROM ObjectCursor INTO #Name;
END
CLOSE ObjectCursor;
DEALLOCATE ObjectCursor;
It won't catch everything (dynamic SQL or latebound objects), but it can be useful - call sp_refreshsqlmodule on all non-schema bound stored procedures (you can call it before to ensure that dependencies are updated and then query the dependencies, or call it afterwards and see if anything is broken):
DECLARE #template AS varchar(max)
SET #template = 'PRINT ''{OBJECT_NAME}''
EXEC sp_refreshsqlmodule ''{OBJECT_NAME}''
'
DECLARE #sql AS varchar(max)
SELECT #sql = ISNULL(#sql, '') + REPLACE(#template, '{OBJECT_NAME}',
QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME))
FROM INFORMATION_SCHEMA.ROUTINES
WHERE OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') IS NULL
OR OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') = 0
EXEC (
#sql
)
In addition to the script from Michael Petito you can check for issues with late-bound objects in SPs (deferred name resolution) like this:
-- Based on comment from http://blogs.msdn.com/b/askjay/archive/2012/07/22/finding-missing-dependencies.aspx
-- Check also http://technet.microsoft.com/en-us/library/bb677315(v=sql.110).aspx
select o.type, o.name, ed.referenced_entity_name, ed.is_caller_dependent
from sys.sql_expression_dependencies ed
join sys.objects o on ed.referencing_id = o.object_id
where ed.referenced_id is null
I basically did the same thing, but wrote it to be CURSORless which is super fast.
DECLARE #Name nvarchar(1000);
DECLARE #Sql nvarchar(1000);
DECLARE #Result int;
DECLARE #Objects TABLE (
Id INT IDENTITY(1,1),
Name nvarchar(1000)
)
INSERT INTO #Objects
SELECT QUOTENAME(SCHEMA_NAME(o.schema_id)) + '.' + QUOTENAME(OBJECT_NAME(o.object_id))
FROM sys.objects o
WHERE type_desc IN (
'SQL_STORED_PROCEDURE',
'SQL_TRIGGER',
'SQL_SCALAR_FUNCTION',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'VIEW')
--include the following if you have schema bound objects since they are not supported
AND ISNULL(OBJECTPROPERTY(o.object_id, 'IsSchemaBound'), 0) = 0
DECLARE #x INT
DECLARE #xMax INT
SELECT #xMax = MAX(Id) FROM #Objects
SET #x = 1
WHILE #x < #xMax
BEGIN
SELECT #Name = Name FROM #Objects WHERE Id = #x
SET #Sql = N'EXEC sp_refreshsqlmodule ''' + #Name + '''';
--PRINT #Sql;
BEGIN TRY
EXEC #Result = sp_executesql #Sql;
IF #Result <> 0 RAISERROR('Failed', 16, 1);
END TRY
BEGIN CATCH
PRINT 'The module ''' + #Name + ''' does not compile.';
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH
SET #x = #x + 1
END
Couple of ways that come to mind
Most obvious way run the procedures
check dependencies on the table before you drop the table or a field. then check out those dependent proceudres
generate scripts on all procedures and search for that field or table
Query sysobjects
Once I made change to a table such as column rename, I have to alter all the stored procedures, functions and views that refer the table column. Obviously I have to manually alter them one by one. But my database contains hundreds of objects like these. So I wanted to make sure I have altered all the depending objects. One solution is to recompile all the objects (via a script). But recompilation happens on each object’s next execution only. But what I want is to validate them and get the details now.
For that I can use “sp_refreshsqlmodule” instead of “sp_recompile”. This will refresh each object and throws an error if its not parsing correctly.
Here is the script below;
-- table variable to store procedure names
DECLARE #tblObjects TABLE (ObjectID INT IDENTITY(1,1), ObjectName
sysname)
-- get the list of stored procedures, functions and views
INSERT INTO #tblObjects(ObjectName)
SELECT '[' + sc.[name] + '].[' + obj.name + ']'
FROM sys.objects obj
INNER JOIN sys.schemas sc ON sc.schema_id = obj.schema_id
WHERE obj.[type] IN ('P', 'FN', 'V') -- procedures, functions, views
-- counter variables
DECLARE #Count INT, #Total INT
SELECT #Count = 1
SELECT #Total = COUNT(*) FROM #tblObjects
DECLARE #ObjectName sysname
-- start the loop
WHILE #Count <= #Total BEGIN
SELECT #ObjectName = ObjectName
FROM #tblObjects
WHERE ObjectID = #Count
PRINT 'Refreshing... ' + #ObjectName
BEGIN TRY
-- refresh the stored procedure
EXEC sp_refreshsqlmodule #ObjectName
END TRY
BEGIN CATCH
PRINT 'Validation failed for : ' + #ObjectName + ', Error:' +
ERROR_MESSAGE() + CHAR(13)
END CATCH
SET #Count = #Count + 1
END
If any object throws an error I can now attend to it and manually fix the issue with it.
None of the answers given can find the error resulting from renaming or dropping a table
but be happy, I have a solution on SQL Server 2017 and higher versions:
DECLARE #NumberRecords INT
DECLARE #RowCount INT
DECLARE #Name NVARCHAR(MAX)
DECLARE #Command NVARCHAR(MAX)
DECLARE #Result int
DECLARE #Names TABLE (
[RowId] INT NOT NULL IDENTITY(1, 1),
[Name] NVARCHAR(MAX),
[Type] NVARCHAR(MAX)
)
INSERT INTO #Names
SELECT
QUOTENAME(SCHEMA_NAME([Objects].schema_id)) + '.' + QUOTENAME(OBJECT_NAME([Objects].object_id)) [Name],
type_desc [Type]
FROM sys.objects [Objects]
WHERE type_desc IN ('SQL_STORED_PROCEDURE',
'SQL_TRIGGER',
'SQL_SCALAR_FUNCTION',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'VIEW')
ORDER BY [Name]
SET #RowCount = 1
SET #NumberRecords = (SELECT COUNT(*) FROM #Names)
WHILE (#RowCount <= #NumberRecords)
BEGIN
SELECT #Name = [Name]
FROM #Names
WHERE [RowId] = #RowCount
SET #Command = N'EXEC sp_refreshsqlmodule ''' + #Name + ''''
BEGIN TRY
EXEC #Result = sp_executesql #Command
IF #Result <> 0
BEGIN
RAISERROR('Failed', 16, 1)
END
ELSE
BEGIN
IF (NOT EXISTS (SELECT *
FROM sys.dm_sql_referenced_entities(#Name, 'OBJECT')
WHERE [is_incomplete] = 1))
BEGIN
DELETE
FROM #Names
WHERE [RowId] = #RowCount
END
END
END TRY
BEGIN CATCH
-- Nothing
END CATCH
SET #RowCount = #RowCount + 1
END
SELECT [Name],
[Type]
FROM #Names
I tried "Cade Roux" Answer , it went wrong and I fixed it as following
SELECT 'BEGIN TRAN T1;' UNION
SELECT REPLACE('BEGIN TRY
EXEC sp_refreshsqlmodule ''{OBJECT_NAME}''
END TRY
BEGIN CATCH
PRINT ''{OBJECT_NAME} IS INVALID.''
END CATCH', '{OBJECT_NAME}',
QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME))
FROM INFORMATION_SCHEMA.ROUTINES
WHERE OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') IS NULL
OR OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') = 0
UNION
SELECT 'ROLLBACK TRAN T1;'
Same idea, but more universal - you check all user defined objects with bodies
And it shows you error during compiling. This is really useful after renaming/removing objects/columns etc
Just run it after database schema update to make sure that all body objects still valid
DECLARE #obj_name AS sysname, #obj_type AS sysname
DECLARE obj_cursor CURSOR FOR
SELECT SCHEMA_NAME(o.schema_id) + '.' + o.name, o.type_desc
FROM sys.objects o
INNER JOIN sys.sql_modules m ON o.object_id = m.object_id
WHERE o.is_ms_shipped = 0 AND m.is_schema_bound = 0
ORDER BY o.type_desc, SCHEMA_NAME(o.schema_id), o.name
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO #obj_name, #obj_type
WHILE (##FETCH_STATUS <> -1)
BEGIN
BEGIN TRY
EXEC sp_refreshsqlmodule #obj_name
--PRINT 'Refreshing ''' + #obj_name + ''' completed'
END TRY
BEGIN CATCH
PRINT 'ERROR - ' + #obj_type + ' ''' + #obj_name + ''':' + ERROR_MESSAGE()
END CATCH
FETCH NEXT FROM obj_cursor INTO #obj_name, #obj_type
END
CLOSE obj_cursor
DEALLOCATE obj_cursor
My approach was a little bit different. I've created alter script for a bunch of procs in SSMS and then waited for few seconds so SSMS process them and Ive got what I wanted:
O then SSMS right border a red dot for any line in error, which I can easily check, correct and later execute same script to update with correct values.