我已经更改了 @Earl 编写的脚本(见上文),以便它执行 CREATE OR ALTER,并且也适用于 SQL Server 2016 及更高版本的模式。
它仍然不完美,因为找到“CREATE”部分并不简单。
set nocount ON
-- MAKE SURE THIS IS ON!!
SET QUOTED_IDENTIFIER ON
DROP TABLE if exists #ProcList
CREATE TABLE #ProcList
(
name sysname NOT NULL /*PRIMARY KEY*/,
SchemaName sysname not null,
id int NOT NULL,
type char(2) NOT NULL,
definition nvarchar(max) NULL,
alterstmt nvarchar(max) NULL,
processed bit NOT NULL,
successful bit NOT null,
primary key clustered (SchemaName, name)
)
--- Build the list of objects that have quoted_identifier off
INSERT INTO #ProcList
SELECT
so.name,
schema_name(so.schema_id),
so.object_id,
so.type,
sm.definition,
NULL,
0,
0
FROM sys.objects so
INNER JOIN sys.sql_modules sm
ON so.object_id = sm.object_id
WHERE
LEFT(so.name, 3) NOT IN ('sp_', 'xp_', 'ms_')
AND sm.uses_quoted_identifier = 0
ORDER BY
so.name
--- Used in try/catch below
DECLARE @ErrorMessage nvarchar(4000);
DECLARE @ErrorSeverity int;
DECLARE @ErrorState int;
DECLARE @name sysname, @SchemaName sysname
declare @type char(2)
DECLARE @objType nvarchar(50)
DECLARE @createCommand nvarchar(max)
DECLARE @dropCommand nvarchar(max)
DECLARE @success bit
-- Get the first object
SELECT top (1) @name = name, @SchemaName= SchemaName FROM #ProcList WHERE processed = 0 order by SchemaName, name
--- As long as we have one, keep going
WHILE (@name IS NOT NULL)
BEGIN
raiserror ('at %s %s', 10, 1, @SchemaName, @name) with nowait
SELECT
@createCommand = definition,
@type = type
FROM #ProcList
WHERE name = @name
--- Determine what type of object it is
SET @objType = CASE @type
WHEN 'P' THEN 'PROCEDURE'
WHEN 'TF' THEN 'FUNCTION'
WHEN 'IF' THEN 'FUNCTION'
WHEN 'FN' THEN 'FUNCTION'
WHEN 'V' THEN 'VIEW'
WHEN 'TR' THEN 'TRIGGER'
END
--- Create the drop command
SET @dropCommand = 'DROP ' + @objType + ' ' + quotename(@SchemaName) + '.' + quotename(@name)
--- record the drop statement that we are going to execute
UPDATE #ProcList
SET
processed = 1,
alterstmt = @dropCommand
WHERE name = @name and SchemaName = @SchemaName
--- Assume we will not succeed
SET @success = 0
BEGIN TRANSACTION
--- Drop the current proc
--EXEC sp_executesql @dropCommand
BEGIN TRY
set @createCommand = replace(@createCommand, 'Create proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create' + char(13) + char(10) + 'proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create' + char(10) + 'proc', 'Create or Alter Proc')
set @createCommand = replace(@createCommand, 'Create view', 'Create or Alter view')
set @createCommand = replace(@createCommand, 'Create view', 'Create or Alter view')
set @createCommand = replace(@createCommand, 'Create view', 'Create or Alter view')
set @createCommand = replace(@createCommand, 'Create view', 'Create or Alter view')
--- Execute the create statement from the definition
EXEC sp_executesql @createCommand
--- If we reached this point, it all worked
SET @success = 1
COMMIT
END TRY
BEGIN CATCH
--- oops something went wrong
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
PRINT 'Error processing ' + @name
exec dbo.LongPrint @createCommand
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @name)
--- Undo the transaction, which undoes the drop above
ROLLBACK
END CATCH
--- At this point, there should be no open transactions
IF @@TRANCOUNT > 0
BEGIN
PRINT 'ERROR... transaction count not right!!'
ROLLBACK
RETURN
END
--- check to make sure the object still exists after executing the alter statement, and that we didn't detect an earlier error
--- If it's all good, then mark the proc as having been successful
IF (
@success = 1
AND EXISTS (
SELECT so.name
FROM sys.objects so
INNER JOIN sys.sql_modules sm
ON so.object_id = sm.object_id
WHERE so.name = @name and schema_name(so.schema_id) = @SchemaName
)
)
BEGIN
UPDATE #ProcList SET successful = 1 WHERE name = @name and SchemaName = @SchemaName
END
SELECT @name = null
-- Get the next one... if none are left the @name will be NULL
SELECT top (1) @name = name, @SchemaName= SchemaName FROM #ProcList WHERE processed = 0 order by SchemaName, name
END
-- What wasn't successful??
SELECT *
FROM #ProcList
WHERE successful = 0
ORDER BY SchemaName, name