Sql sp_MSForEachDB在函数中使用的副作用运算符无效
无论如何,是否可以将下面的代码放入表值(内联或多语句)甚至视图中。我可以在存储过程中使用它,但由于无法连接存储过程的结果,我宁愿将它放在表值函数或视图中 下面是SQL,它提供了我要查找的结果Sql sp_MSForEachDB在函数中使用的副作用运算符无效,sql,sql-server-2008,tsql,dynamic,metadata,Sql,Sql Server 2008,Tsql,Dynamic,Metadata,无论如何,是否可以将下面的代码放入表值(内联或多语句)甚至视图中。我可以在存储过程中使用它,但由于无法连接存储过程的结果,我宁愿将它放在表值函数或视图中 下面是SQL,它提供了我要查找的结果 DECLARE @ColumnInformation TABLE ( DatabaseName NVARCHAR(255), TableSchema NVARCHAR(255), TableName NVARCHAR(255), ColumnName NVARCHAR(255
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上每个数据库的所有信息SCHEMA.COLUMNS的结果。如果没有动态SQL,就无法动态地执行此操作(如不预先硬编码所有数据库名称),也无法在函数中使用动态SQL。你或者,我的朋友 编写一个返回结果集的存储过程。如果您确实需要将输出连接到其他内容(为什么不将其编码到存储过程?),则将输出插入#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...
问题的一部分是“如果上面的答案是否定的……有没有办法重写语句,使其不使用动态SQL?”我知道spMSForEachDB将来可能会被弃用,如果是的话,我只需要编写我自己的版本来完成我需要它做的事情。另外,您提到的sp_MSForEachDB的替换在我的场景中不起作用。除非我注释掉USE[?]行,否则它会爆炸。如果这被注释掉了,我只从主数据库或我当前使用的数据库中获取信息;印刷品[SomeDatabase'不存在。请确保输入的名称正确。当我尝试将Insert语句与修改后的sp_foreachdb一起使用时,会出现另一个错误。Msg 8164,第16级,状态1,过程sp_foreachdb,第86行Insert EXEC语句无法嵌套。因此,它不是MS proc的真正替代品。而且,不…不名称中的uare括号。打印的消息在第二行的名称周围放上括号,在第三行的右侧括号中省略。谢谢,这似乎符合我的预期。除了将存储过程的结果重新插入临时表的性能开销之外,我喜欢它……但如果这是唯一的原因我想这是唯一的办法。我会给它一天的时间,除非我看到更好的答案,否则我会接受这个。顺便问一下,你为什么要把所有的校对SQL\u Latin\u General\u CP1\u CI\u AI放在所有地方?你担心性能开销?你多久运行一次?通常性能开销不是不频繁的一次性管理员最关心的分层/维护任务。存在排序规则引用,因为如果您有具有不同排序规则的数据库,元数据将产生错误,例如
Msg 451无法解决之间的排序规则冲突…
是的,我想您关于性能的看法是正确的…这真的不应该太令人担心。感谢有关COLL的信息ATE我不会一次处理多个数据库。也感谢您包含SYSNAME数据类型。我不知道该类型,只是使用nvarchar(255)来处理这些数据。