问题
Is there anyway to put the below code into a Table-Valued (inline or multi-statement) or even a View. I'm able to use it in a stored procedure but do to the inability to do joins to the results of stored procedures I'd rather put it in a Table-Valued function or View.
Below is the SQL that gives me the results I'm looking for
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
however when I try to put it in a table valued function I'm getting the error
Msg 443, Level 16, State 14, Procedure fGetAllColumnInformation, Line 17
Invalid use of a side-effecting operator 'INSERT EXEC' within a function.
I believe that sp_MSForEachDb is generating dynamic SQL but not real sure. I vaguely remember hearing that dynamic SQL can not be used in table-valued functions. If this is the case is there anyway of bypassing that restriction so that I can use the above code in a table-valued function or view.
If the answer to the above is no...is there any way of rewriting the statement so that it does not use dynamic SQL?
I'm basically trying to union the results of all of the INFORMATION_SCHEMA.COLUMNS from every database on a SQL Server.
回答1:
You can't do this dynamically (as in without hard-coding all the database names in advance) without dynamic SQL, and you can't use dynamic SQL in a function. You shouldn't be using sp_MSForEachDB in any case or, IMHO, using INFORMATION_SCHEMA.
Write a stored procedure that returns the result set. If you absolutely need to join the output to other stuff (why not code that into the stored procedure?), then insert the output into a #temp table.
Try this:
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
Usage:
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...
来源:https://stackoverflow.com/questions/17840506/sp-msforeachdb-invalid-use-of-side-effecting-operator-within-function