有没有办法将来自另一个数据库的视图或过程定义的完整 DDL 存储在单独的 table 中
Is there a way to store full DDL of View or Procedure definition from another database in separate table
我正在尝试将某些视图和存储过程的 DDL 存储在 Dump
数据库中单独的 table 中。服务器上有太多类似的数据库。但是有些对象是多余的。它们将列在 table 中,然后从数据库中删除。但是如果以后有人需要它们,它们的 DDL 将被备份。
该过程在视图较小时工作正常,但如果代码的大小超过某个值 - 我会收到错误消息:
XML parsing: line 120, character 31, incorrect CDATA section syntax
也许那是我正在使用 dbo.sp_sqlexec
程序,但我不确定。将不胜感激任何想法。
定义 table 将首先列出然后存储这些视图的位置:
CREATE TABLE [dbo].[ViewList_Explicit]
(
[ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[ServerName] [sysname] NOT NULL,
[DatabaseName] [sysname] NOT NULL,
[SchemaName] [sysname] NOT NULL,
[ViewName] [sysname] NOT NULL,
[DefinitionText] [xml] NULL,
[IsTransferred] [bit] NOT NULL,
[DateTransferred] [datetime] NULL
);
INSERT INTO [dbo].[ViewList_Explicit] ([ServerName], [DatabaseName], [SchemaName], [ViewName], [DefinitionText], [IsTransferred], [DateTransferred])
VALUES ('dbserver', 'reco', 'dbo', 'v_redundant_toDrop', NULL, 0, NULL)
这是程序的代码:
CREATE OR ALTER PROCEDURE [dbo].[sp_moveViews2Dump]
(@DatabaseName SYSNAME)
AS
BEGIN
SET NOCOUNT ON
DECLARE @Serv SYSNAME = @@SERVERNAME;
DECLARE @SQLstringToDrop NVARCHAR(MAX), @SQLstringForDefinition NVARCHAR(MAX);
DECLARE @SchemaName SYSNAME, @ViewName SYSNAME, @ExplicitID INTEGER;
DECLARE @DDLview XML;
DECLARE @Buffer TABLE(line XML);
DECLARE Schedule_cursor CURSOR LOCAL FOR
SELECT ID, SchemaName, ViewName
FROM [Dump].dbo.ViewList_Explicit
WHERE DatabaseName = @DatabaseName
AND ServerName = @Serv
AND IsTransferred = 0
OPEN Schedule_cursor
FETCH NEXT FROM Schedule_cursor INTO @ExplicitID, @SchemaName, @ViewName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQLstringForDefinition = 'SELECT CONCAT(''<query><![CDATA['', VIEW_DEFINITION, '']]></query>'') FROM ['
+ @DatabaseName + '].INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = '''+ @ViewName + ''' AND TABLE_SCHEMA = ''' + @SchemaName + ''';'
--PRINT @SQLstringForDefinition
INSERT @Buffer EXECUTE dbo.sp_sqlexec @SQLstringForDefinition
SELECT @DDLview = line FROM @Buffer
SELECT @SQLstringToDrop = 'USE [' + @DatabaseName + ']
DROP VIEW IF EXISTS [' + @SchemaName + '].[' + @ViewName + ']'
--EXECUTE dbo.sp_sqlexec @SQLstringToDrop -- Commented out to avoid the deletion
UPDATE [Dump].dbo.ViewList_Explicit
SET [DefinitionText] = @DDLview, IsTransferred = 1, DateTransferred = GETDATE()
WHERE ID = @ExplicitID
DELETE FROM @Buffer
FETCH NEXT FROM Schedule_cursor INTO @ExplicitID, @SchemaName, @ViewName
END
CLOSE Schedule_cursor
DEALLOCATE Schedule_cursor
SET NOCOUNT OFF
END
不确定为什么要将模块定义存储为 XML,但您应该能够一步完成,没有光标、几十年前不受支持的系统过程,以及 INFORMATION_SCHEMA
是 generally garbage(<-- 请参阅 Module Definitions
部分):
DECLARE @exec nvarchar(1024) = QUOTENAME(@DatabaseName)
+ N'.sys.sp_executesql';
DECLARE @sql nvarchar(max) = N';WITH vws AS
(SELECT SchemaName = s.name, ViewName = v.name,
DefinitionText = CONCAT(''<query><![CDATA['',
OBJECT_DEFINITION(v.[object_id]), N'']]></query>'')
FROM sys.schemas AS s
INNER JOIN sys.views AS v
ON s.[schema_id] = v.[schema_id]
)
UPDATE vle
SET vle.DefinitionText = sps.DefinitionText,
vle.IsTransferred = 1,
vle.DateTransferred = GETDATE()
FROM [Dump].dbo.ViewList_Explicit AS vle
INNER JOIN vws
ON vle.SchemaName = vws.SchemaName
AND vle.ViewName = vws.ViewName
WHERE vle.DatabaseName = @db
AND vle.ServerName = @@SERVERNAME
AND vle.IsTransferred = 0;';
EXEC @exec @sql, N'@db sysname', @DatabaseName;
评论中已经提到了主要问题:VIEW_DEFINITION
限制为 4,000 个字符。
@SQLstringToDrop
的目的不明确。如果您使用的是足够现代的 SQL 服务器版本,则可以将 CREATE OR ALTER
注入定义,或者在实际执行时生成 only /部署...每个视图都不会更改,因此没有理由为每个视图存储整个 IF EXISTS / DROP
序列。
如果您想在备份定义后删除视图(尽管,实际上,为什么您没有为此使用适当的版本控制系统是个谜),您可以简单地使用相同的技术(全部一次而不是循环):
DECLARE @exec nvarchar(1024) = QUOTENAME(@DatabaseName)
+ N'.sys.sp_executesql';
DECLARE @sql nvarchar(max) = N'';
SELECT @sql += CONCAT(N'DROP VIEW IF EXISTS ',
QUOTENAME(SchemaName), N'.',
QUOTENAME(ViewName), N';')
FROM [Dump].dbo.ViewList_Explicit
WHERE DatabaseName = @DatabaseName
AND ServerName = @@SERVERNAME;
EXEC @exec @sql;
作为一个额外的提示,永远不要手动将方括号放在名称周围(例如 [' + @SchemaName + ']
)——这不能保护您免受 SQL 注入。虽然不太可能有人将恶意对象名称放入您现在正在使用的系统中,但它树立了一个坏榜样。
我正在尝试将某些视图和存储过程的 DDL 存储在 Dump
数据库中单独的 table 中。服务器上有太多类似的数据库。但是有些对象是多余的。它们将列在 table 中,然后从数据库中删除。但是如果以后有人需要它们,它们的 DDL 将被备份。
该过程在视图较小时工作正常,但如果代码的大小超过某个值 - 我会收到错误消息:
XML parsing: line 120, character 31, incorrect CDATA section syntax
也许那是我正在使用 dbo.sp_sqlexec
程序,但我不确定。将不胜感激任何想法。
定义 table 将首先列出然后存储这些视图的位置:
CREATE TABLE [dbo].[ViewList_Explicit]
(
[ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[ServerName] [sysname] NOT NULL,
[DatabaseName] [sysname] NOT NULL,
[SchemaName] [sysname] NOT NULL,
[ViewName] [sysname] NOT NULL,
[DefinitionText] [xml] NULL,
[IsTransferred] [bit] NOT NULL,
[DateTransferred] [datetime] NULL
);
INSERT INTO [dbo].[ViewList_Explicit] ([ServerName], [DatabaseName], [SchemaName], [ViewName], [DefinitionText], [IsTransferred], [DateTransferred])
VALUES ('dbserver', 'reco', 'dbo', 'v_redundant_toDrop', NULL, 0, NULL)
这是程序的代码:
CREATE OR ALTER PROCEDURE [dbo].[sp_moveViews2Dump]
(@DatabaseName SYSNAME)
AS
BEGIN
SET NOCOUNT ON
DECLARE @Serv SYSNAME = @@SERVERNAME;
DECLARE @SQLstringToDrop NVARCHAR(MAX), @SQLstringForDefinition NVARCHAR(MAX);
DECLARE @SchemaName SYSNAME, @ViewName SYSNAME, @ExplicitID INTEGER;
DECLARE @DDLview XML;
DECLARE @Buffer TABLE(line XML);
DECLARE Schedule_cursor CURSOR LOCAL FOR
SELECT ID, SchemaName, ViewName
FROM [Dump].dbo.ViewList_Explicit
WHERE DatabaseName = @DatabaseName
AND ServerName = @Serv
AND IsTransferred = 0
OPEN Schedule_cursor
FETCH NEXT FROM Schedule_cursor INTO @ExplicitID, @SchemaName, @ViewName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQLstringForDefinition = 'SELECT CONCAT(''<query><![CDATA['', VIEW_DEFINITION, '']]></query>'') FROM ['
+ @DatabaseName + '].INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = '''+ @ViewName + ''' AND TABLE_SCHEMA = ''' + @SchemaName + ''';'
--PRINT @SQLstringForDefinition
INSERT @Buffer EXECUTE dbo.sp_sqlexec @SQLstringForDefinition
SELECT @DDLview = line FROM @Buffer
SELECT @SQLstringToDrop = 'USE [' + @DatabaseName + ']
DROP VIEW IF EXISTS [' + @SchemaName + '].[' + @ViewName + ']'
--EXECUTE dbo.sp_sqlexec @SQLstringToDrop -- Commented out to avoid the deletion
UPDATE [Dump].dbo.ViewList_Explicit
SET [DefinitionText] = @DDLview, IsTransferred = 1, DateTransferred = GETDATE()
WHERE ID = @ExplicitID
DELETE FROM @Buffer
FETCH NEXT FROM Schedule_cursor INTO @ExplicitID, @SchemaName, @ViewName
END
CLOSE Schedule_cursor
DEALLOCATE Schedule_cursor
SET NOCOUNT OFF
END
不确定为什么要将模块定义存储为 XML,但您应该能够一步完成,没有光标、几十年前不受支持的系统过程,以及 INFORMATION_SCHEMA
是 generally garbage(<-- 请参阅 Module Definitions
部分):
DECLARE @exec nvarchar(1024) = QUOTENAME(@DatabaseName)
+ N'.sys.sp_executesql';
DECLARE @sql nvarchar(max) = N';WITH vws AS
(SELECT SchemaName = s.name, ViewName = v.name,
DefinitionText = CONCAT(''<query><![CDATA['',
OBJECT_DEFINITION(v.[object_id]), N'']]></query>'')
FROM sys.schemas AS s
INNER JOIN sys.views AS v
ON s.[schema_id] = v.[schema_id]
)
UPDATE vle
SET vle.DefinitionText = sps.DefinitionText,
vle.IsTransferred = 1,
vle.DateTransferred = GETDATE()
FROM [Dump].dbo.ViewList_Explicit AS vle
INNER JOIN vws
ON vle.SchemaName = vws.SchemaName
AND vle.ViewName = vws.ViewName
WHERE vle.DatabaseName = @db
AND vle.ServerName = @@SERVERNAME
AND vle.IsTransferred = 0;';
EXEC @exec @sql, N'@db sysname', @DatabaseName;
评论中已经提到了主要问题:VIEW_DEFINITION
限制为 4,000 个字符。
@SQLstringToDrop
的目的不明确。如果您使用的是足够现代的 SQL 服务器版本,则可以将 CREATE OR ALTER
注入定义,或者在实际执行时生成 only /部署...每个视图都不会更改,因此没有理由为每个视图存储整个 IF EXISTS / DROP
序列。
如果您想在备份定义后删除视图(尽管,实际上,为什么您没有为此使用适当的版本控制系统是个谜),您可以简单地使用相同的技术(全部一次而不是循环):
DECLARE @exec nvarchar(1024) = QUOTENAME(@DatabaseName)
+ N'.sys.sp_executesql';
DECLARE @sql nvarchar(max) = N'';
SELECT @sql += CONCAT(N'DROP VIEW IF EXISTS ',
QUOTENAME(SchemaName), N'.',
QUOTENAME(ViewName), N';')
FROM [Dump].dbo.ViewList_Explicit
WHERE DatabaseName = @DatabaseName
AND ServerName = @@SERVERNAME;
EXEC @exec @sql;
作为一个额外的提示,永远不要手动将方括号放在名称周围(例如 [' + @SchemaName + ']
)——这不能保护您免受 SQL 注入。虽然不太可能有人将恶意对象名称放入您现在正在使用的系统中,但它树立了一个坏榜样。