2013-07-24 13 views
2

無論如何將下面的代碼放入Table-Valued(內聯或多語句)甚至視圖中。我可以在存儲過程中使用它,但要做到無法連接到存儲過程的結果,我寧願將它放在Table-Valued函數或View中。sp_MSForEachDB函數中的副作用運算符無效

下面是給我的結果的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的結果。

+0

好部分是「如果答案上面沒有...有沒有重寫語句,以任何方式它不使用動態SQL?「我知道spMSForEachDB將來可能會被棄用,如果是這樣的話,我只需要寫我自己的就可以做我需要的。 –

+0

另外,您提到的http://www.mssqltips.com/sqlservertip/2201/making-a-more-reliable-and-flexible-spmsforeachdb/替換sp_MSForEachDB在我的方案中不起作用。除非我將USE [?]行註釋掉,否則它會彈出。如果這被註釋掉了,我只能從master數據庫獲得信息,或者我正在使用哪個數據庫。 –

+0

我收到Msg 911,Level 16,State 1,Line 4 Database'[SomeDatabase]; \t \t PRINT'[SomeDatabase'不存在。確保名稱輸入正確。 –

回答

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... 
問題的
+0

感謝這似乎符合我的意圖。除了將存儲過程的結果重新插入到臨時表中的性能開銷之外,我喜歡它......但是如果這是唯一的方法,我想這是唯一的方法,除非我看到更好的答案,否則我會給它1天你會接受這個嗎?順便說一句,爲什麼你要把所有的COLLATE SQL_Latin1_General_CP1_CI_AI放到任何地方? –

+0

你擔心性能開銷?你多久運行一次?通常性能開銷並不是偶發的一次性管理/維護任務 –

+0

歸類引用存在,因爲如果您擁有不同歸類的數據庫,元數據將會ld出現錯誤,比如'Msg 451無法解決......之間的排序規則衝突' –