sp_MSForEachDB在函数内使用副作用运算符无效

时间:2013-07-24 17:04:34

标签: sql sql-server-2008 tsql dynamic metadata

无论如何将下面的代码放入Table-Value(内联或多语句)甚至View中。我能够在存储过程中使用它但是无法对存储过程的结果进行连接,而是将其放在Table-Valued函数或View中。

下面是SQL,它为我提供了我正在寻找的结果

DECLARE @ColumnInformation TABLE
(
    DatabaseName NVARCHAR(255),
    TableSchema NVARCHAR(255),
    TableName NVARCHAR(255),
    ColumnName NVARCHAR(255),
    TableType NVARCHAR(255),
    FullyQualifiedTableName NVARCHAR(255),
    FullyQualifiedColumnName NVARCHAR(255)
)
INSERT INTO @ColumnInformation
EXECUTE master.sys.sp_MSForEachDB  '
IF ''?'' NOT IN (''master'', ''tempdb'', ''msdb'', ''model'', ''ReportServer'', ''ReportServerTempDB'' )
BEGIN
    USE [?];
    PRINT ''?''
    SELECT 
         T.TABLE_CATALOG AS DatabaseName
        ,T.TABLE_SCHEMA as TableSchema  
        ,T.TABLE_NAME AS TableName
        ,C.COLUMN_NAME AS ColumnName
        ,T.TABLE_TYPE AS TableType
        ,''['' + T.TABLE_CATALOG + ''].['' + T.TABLE_SCHEMA + ''].['' + T.TABLE_NAME + '']'' AS FullyQualifiedTableName
        ,''['' + T.TABLE_CATALOG + ''].['' + T.TABLE_SCHEMA + ''].['' + T.TABLE_NAME + ''].['' + C.COLUMN_NAME + '']'' FullyQualifiedColumnName
    FROM INFORMATION_SCHEMA.TABLES as t inner join
        INFORMATION_SCHEMA.COLUMNS as c on c.table_name = t.table_name
END
'

SELECT * 
FROM @ColumnInformation
ORDER BY DatabaseName, TableSchema, TableName, ColumnName

然而,当我尝试将它放在一个表值函数中时,我得到了错误

Msg 443, Level 16, State 14, Procedure fGetAllColumnInformation, Line 17
Invalid use of a side-effecting operator 'INSERT EXEC' within a function.

我相信sp_MSForEachDb正在生成动态SQL但不确定。我依稀记得听说动态SQL不能用于表值函数。如果是这种情况,那么无论如何都要绕过该限制,以便我可以在表值函数或视图中使用上述代码。

如果上面的答案是否...是否有任何方法可以重写语句,以便它不使用动态SQL?

我基本上试图将SQL Server上每个数据库的所有INFORMATION_SCHEMA.COLUMNS的结果联合起来。

1 个答案:

答案 0 :(得分:2)

如果没有动态SQL,则无法动态执行此操作(如未事先对所有数据库名称进行硬编码),并且无法在函数中使用动态SQL。您shouldn't be using sp_MSForEachDB in any case或恕我直言,using INFORMATION_SCHEMA

编写一个返回结果集的存储过程。如果你绝对需要将输出连接到其他东西(为什么不将它编码到存储过程?),然后将输出插入到#temp表中。

试试这个:

CREATE PROCEDURE dbo.AllMyColumnsEverywhereForReals
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql NVARCHAR(MAX) = N'';

  SELECT @sql += '
  UNION ALL SELECT 
      [database]  = N''' + d.name + ''' COLLATE SQL_Latin1_General_CP1_CI_AI,
      [schema]    = s.name COLLATE SQL_Latin1_General_CP1_CI_AI,
      [object]    = o.name COLLATE SQL_Latin1_General_CP1_CI_AI,
      [column]    = c.name COLLATE SQL_Latin1_General_CP1_CI_AI,
      [qualified] = QUOTENAME(''' + d.name + ''') 
        + ''.'' + QUOTENAME(s.name) 
        + ''.'' + QUOTENAME(c.name) COLLATE SQL_Latin1_General_CP1_CI_AS,
      [type] = CASE o.type WHEN ''U'' THEN ''Table'' ELSE ''View'' END
    FROM ' + QUOTENAME(d.name) + '.sys.columns AS c
      INNER JOIN ' + QUOTENAME(d.name) + '.sys.objects AS o
      ON c.[object_id] = o.[object_id]
      INNER JOIN ' + QUOTENAME(d.name) + '.sys.schemas AS s
      ON o.[schema_id] = s.[schema_id]
      WHERE o.type IN (''U'', ''V'')'
  FROM sys.databases AS d WHERE [state] = 0 AND name NOT IN 
    (N'master',N'tempdb',N'msdb',N'model',N'ReportServer',N'ReportServerTempDB');

  SET @sql = STUFF(@sql, 1, 13, '');

  EXEC sp_executesql @sql;
END
GO

用法:

CREATE TABLE #x
(
  db     SYSNAME, 
  sch    SYSNAME, 
  obj    SYSNAME, 
  col    SYSNAME, 
  qual   NVARCHAR(390),
  [type] CHAR(5)
);

INSERT #x EXEC dbo.AllMyColumnsEverywhereForReals;

SELECT cols FROM #x AS x -- INNER JOIN something else ON x.whatever...