2012-04-23 54 views
0

我最近发现了一个问题,我希望使用sp_MSforeachtable存储过程来选择表名中包含单词Transcode的所有表,并在这些表上运行一些SQL。我设法编写了一些可行的代码,但并不完美 - 对于那些我希望能够优雅地跳过的表(即那些没有代码转换代码的表),它反而会抛出由于某些预期的列造成的错误(只有存在于转码表中)不存在于这些表上。问题似乎是调用存储过程时解析所有SQL,而不是只在需要时解析SQL(例如满足条件时)。sp_MSforeachtable - 解析动态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 
' 

然而,当我再尝试添加功能,我得到的代码错误,这些错误将永远不会运行;例如

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 
' 

这一次我得到的输出作为第一个为那些表名包含单词转码相同,但对于那些它并不代替见状忽略,我看到:

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

无效的列名COL3

我敢肯定这是倒在动态SQL被解析的方式,但这是不受欢迎的行为。有没有人遇到过/有没有简单的解决方法?

这不是紧急的,因为在我的情况下,由于列不存在,错误与if语句具有相同的效果,并且有效的行能够成功运行,但是我希望在I需要尽快做类似的事情,这种行为会导致问题。

由于提前,

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 

回答

3

其中之一,我建议远离无证和不受支持的程序,如sp_MSForEachTable。它们可以随时更改,甚至可以从SQL Server中删除,并且此特定过程可能与许多人针对sp_MSForEachDb报告的症状相同。(见一些背景herehere,而证据表明,他们没有固定,记录或支持它here的意向。)

这里是我会怎么做:

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; 

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

+0

非常感谢亚伦 - 简单得多:)。 PS。我还发现另一个选项 - 嵌套动态SQL(Mikael Eriksson的答案在这里: http://stackoverflow.com/questions/9679997/sql-server-sp-msforeachtable-usage-to-select-only-those-tables-which- meet-some-c) - 但正如你指出的那样,如果proc不支持,最好不要使用。再次感谢,JB – JohnLBevan 2012-04-23 14:03:34

0

可以使用@whereand参数,所以你不需要在你的代码进行检查。

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

更新:

我敢肯定这是倒在动态SQL被解析的方式,但 它的不良行为。有没有人遇到过这个

当然可以。代码在执行之前编译并且编译器检查针对该表的insert语句中使用的列名。

+0

感谢Mikael;还没有发现有其他参数 - 我现在看看这个存储过程的代码,所以我们已经学习了更多关于下面的内容。 – JohnLBevan 2012-04-23 14:11:43

0

问题在于解析器。无论您是否在某些条件下使用sp_msforeachtable,仍会解析每个表。所以,对于其他表格 - 抛出错误。您可以使用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 
' 
+0

谨慎的是,我会小心诸如'insert?'之类的事情 - 这只是表名,如果模式不是'dbo'呢?如果表格被命名为“表格”或“我的表格”呢? – 2012-04-23 14:05:32

+0

谢谢你的评论。亚伦 - 关于模式和特殊字符,我在插入语句之前做了检查,看看我是否应该追加模式/使用quotename函数 - 存储的proc返回吗?形式为[dbo]。[my-table];所以工作正常。同样在这种情况下,我知道在其他模式中不会有Transcode表;但是对于线程后面的任何人来说都是有效的点 – JohnLBevan 2012-04-23 14:17:19

+0

是的。 ?被替换为类似[schema_name]。[table_name]的东西。因此,如果约翰必须具体说明,请将语句替换为 - 如果存在(选择1,其中''?''=''[dbo]。[转码]'') – GMaster9 2012-04-23 14:38:13