有没有办法将来自另一个数据库的视图或过程定义的完整 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_SCHEMAgenerally 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 注入。虽然不太可能有人将恶意对象名称放入您现在正在使用的系统中,但它树立了一个坏榜样。