sp_MSforeachtable-动态sql的解析

sp_MSforeachtable-动态sql的解析,sql,sql-server,parsing,stored-procedures,sp-msforeachtable,Sql,Sql Server,Parsing,Stored Procedures,Sp Msforeachtable,我最近发现了一个问题,我想使用sp_MSforeachtable存储过程来选择表名中包含单词Transcode的所有表,并在这些表上运行一些SQL。我成功地编写了一些代码,但并不完美——对于那些我希望它能够优雅地跳过的表(即名称中没有转码的表),由于某些预期列(仅存在于转码表中)不存在于这些表上,它反而抛出了错误。问题似乎是在调用存储过程时解析所有SQL,而不是仅在需要时(例如,当满足条件时)解析SQL 以下代码按预期工作: exec sp_MSforeachtable ' print ''Ta

我最近发现了一个问题,我想使用sp_MSforeachtable存储过程来选择表名中包含单词Transcode的所有表,并在这些表上运行一些SQL。我成功地编写了一些代码,但并不完美——对于那些我希望它能够优雅地跳过的表(即名称中没有转码的表),由于某些预期列(仅存在于转码表中)不存在于这些表上,它反而抛出了错误。问题似乎是在调用存储过程时解析所有SQL,而不是仅在需要时(例如,当满足条件时)解析SQL

以下代码按预期工作:

exec sp_MSforeachtable '
print ''Table being tested: ?''
if exists (select 1 where ''?'' like ''%Transcode%'')
begin
    print ''    Do Something''
end
else
begin
    print ''    Ignored''
end
'
然而,当我尝试添加功能时,我从永远不会运行的代码中得到错误;e、 g

exec sp_MSforeachtable '
print ''Table being tested: ?''
if exists (select 1 where ''?'' like ''%Transcode%'')
begin
    print ''    Do Something''
    
    insert ? (col1, col2, col3)
    select col1, col2, 1
    from ?
    where col3 = 0
    
end
else
begin
    print ''    Ignored''
end
'
这一次,对于那些tablename包含单词Transcode的对象,我得到了与第一次相同的输出,但是对于那些它没有看到而不是看到被忽略的对象,我看到:

消息207,16级,状态1,第9行

列名称col3无效

我很确定这是动态SQL的解析方式,但这是不受欢迎的行为。以前有没有人遇到过这个问题/有没有简单的解决方法

这不像我的情况那样紧急,因为列不存在。无论如何,错误与if语句具有相同的效果,并且有效行能够成功运行,但我渴望了解,如果我需要尽快执行类似操作,这种行为将导致问题

提前感谢,

JB

ps.复制此行为的代码包括以下内容:

create table DemoTranscode1 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null, col3 bit not null)
go
create table DemoTable1 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null)
go
create table DemoTranscode2 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null, col3 bit not null)
go
create table DemoTranscode3 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null, col3 bit not null)
go
insert DemoTranscode1
select 'example1', 'demo', 0
union select 'example2', 'demo', 0
union select 'example3', 'demo', 0
union select 'example4', 'demo', 0
insert DemoTable1 select col1, col2 from DemoTranscode1
insert DemoTranscode2 select col1, col2, col3 from DemoTranscode1
insert DemoTranscode3 select col1, col2, col3 from DemoTranscode1

您可以使用
@wherend
参数,因此无需签入代码

exec sp_MSforeachtable
  @Command1 = 'print "?"',
  @whereand = ' and o.name like ''%Transcode%'''
更新:

我很确定这取决于动态SQL的解析方式,但是 这是不受欢迎的行为。以前有人见过这个吗


当然。代码在执行之前进行编译,编译器将对照表检查insert语句中使用的列名。

您可以使用
@wherend
参数,因此不需要签入代码

exec sp_MSforeachtable
  @Command1 = 'print "?"',
  @whereand = ' and o.name like ''%Transcode%'''
更新:

我很确定这取决于动态SQL的解析方式,但是 这是不受欢迎的行为。以前有人见过这个吗


当然。代码在执行之前进行编译,编译器根据表检查insert语句中使用的列名。

首先,我建议远离未记录和不受支持的过程,如
sp\MSForEachTable
。它们可以随时更改甚至从SQL Server中删除,并且此特定过程可能具有许多针对
sp_MSForEachDb
报告的相同症状。(参见一些背景和证据,表明他们无意对其进行修复、记录或支持。)

我会这样做:

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

SELECT @sql = @sql + 'INSERT ' 
  + QUOTENAME(SCHEMA_NAME([schema_id]))
  + '.' + QUOTENAME(name) + ' (col1, col2, col3)
  SELECT col1, col2, 1 FROM '
  + QUOTENAME(SCHEMA_NAME([schema_id]))
  + '.' + QUOTENAME(name)
  + ' WHERE col3 = 0;'
FROM sys.tables 
WHERE name LIKE '%Transcode%';

PRINT @sql;
-- EXEC sp_executesql @sql;

这样做的好处是,在执行之前很容易验证输出。

首先,我建议远离未记录和不受支持的过程,如
sp\MSForEachTable
。它们可以随时更改甚至从SQL Server中删除,并且此特定过程可能具有许多针对
sp_MSForEachDb
报告的相同症状。(参见一些背景和证据,表明他们无意对其进行修复、记录或支持。)

我会这样做:

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

SELECT @sql = @sql + 'INSERT ' 
  + QUOTENAME(SCHEMA_NAME([schema_id]))
  + '.' + QUOTENAME(name) + ' (col1, col2, col3)
  SELECT col1, col2, 1 FROM '
  + QUOTENAME(SCHEMA_NAME([schema_id]))
  + '.' + QUOTENAME(name)
  + ' WHERE col3 = 0;'
FROM sys.tables 
WHERE name LIKE '%Transcode%';

PRINT @sql;
-- EXEC sp_executesql @sql;

这样做的好处是在执行之前很容易验证输出。

问题在于解析器。无论您是否使用带有某些条件的spmsforeachTable,它仍然会为每个表进行解析。所以,对于其他表-抛出错误。您可以使用exec语句来避免它,如下所示-

exec sp_MSforeachtable ' 
print ''Table being tested: ?'' 
if exists (select 1 where ''?'' like ''%Transcode%'') 
begin 
    print ''    Do Something'' 

    exec ( ''insert ? (col1, col2, col3) select col1, col2, 1 from ? where col3 = 0 '') 

end 
else 
begin 
    print ''    Ignored'' 
end 
' 

问题在于解析器。无论您是否使用带有某些条件的spmsforeachTable,它仍然会为每个表进行解析。所以,对于其他表-抛出错误。您可以使用exec语句来避免它,如下所示-

exec sp_MSforeachtable ' 
print ''Table being tested: ?'' 
if exists (select 1 where ''?'' like ''%Transcode%'') 
begin 
    print ''    Do Something'' 

    exec ( ''insert ? (col1, col2, col3) select col1, col2, 1 from ? where col3 = 0 '') 

end 
else 
begin 
    print ''    Ignored'' 
end 
' 

很好,谢谢亚伦-简单多了:)。另外,我还发现了另一种选择——嵌套动态sql(Mikael Eriksson在这里的答案是:)——但正如您所指出的,如果该过程不受支持,最好不要使用。再次感谢,JBGreat,谢谢Aaron-更简单:)。另外,我还发现了另一种选择——嵌套动态sql(Mikael Eriksson在这里的答案是:)——但正如您所指出的,如果该过程不受支持,最好不要使用。再次感谢,jb作为警告,我会小心处理
insert?
-这只是表名,如果模式不是
dbo
?如果表名为
a table
my table
,该怎么办?谢谢您的评论。Aaron-关于模式和特殊字符,我在插入insert语句之前做了一个检查,看看是否应该附加模式/使用quotename函数-存储的proc返回?形式为[dbo].[my table];所以这是正确的。同样在这种情况下,我知道在其他模式中不会有转码表;但这对任何人来说都是一个有效的观点。是的?替换为类似于[schema\u name].[table\u name]的内容。因此,如果John必须是特定的,那么将语句替换为-if exists(选择1,其中“”?“”=“”[dbo].[Transcode]'')作为警告,我会小心处理
insert?
-这只是表名,如果模式不是
dbo
?如果表名为
a table
my table
,该怎么办?谢谢您的评论。Aaron-关于模式和特殊字符,我在插入insert语句之前做了一个检查,看看是否应该附加模式/使用quotename函数-存储的proc返回?形式为[dbo].[my table];所以这是正确的。同样在这种情况下,我知道在其他模式中不会有转码表;但这对任何人来说都是一个有效的观点。是的?替换为类似于[schema\u name].[table\u name]的内容。所以,如果约翰必须被指定