是否可以基于包含表名的另一张表更新许多表?

时间:2019-06-25 18:05:40

标签: sql sql-server dynamic-sql

当前正在做一个项目,我必须更新85个表上的数据,将当前的空字符串替换为NULL值。 这是一个简单的SQL查询,但是由于它是一个敏感的环境,因此如果出现任何问题,我们需要恢复该状态。

主要思想是创建一个表来保存要回滚的数据。但我试图避免创建85个表。

我将举一个较小的示例:

有4张桌子


   ------------------------------------
   |            airplane              |
   ------------------------------------
   | air_ID | color | tail_number     |
   ------------------------------------
   |  1     | red   |                 |
   |  2     | green |                 |
   |  3     | black |  21AF           |
   ------------------------------------
   ------------------------------------
   |            bus                   |
   ------------------------------------
   | bus_ID | color | tag_number      |
   ------------------------------------
   |  1     | red   |  AAY-464        |
   |  2     | green |                 |
   |  3     | black |                 |
   ------------------------------------
   ------------------------------------
   |            train                 |
   ------------------------------------
   | tr_ID  | color | designated_name |
   ------------------------------------
   |  1     | red   |  99212          |
   |  2     | green |                 |
   |  3     | black |                 |
   ------------------------------------
   ------------------------------------
   |         Cruise_Ship              |
   ------------------------------------
   | sea_ID | color | hull_number     |
   ------------------------------------
   |  1     | red   |                 |
   |  2     | green |  MAGDA          |
   |  3     | black |                 |
   ------------------------------------

所以我用数据创建了一个临时表


    -------------------------------------------------
    |         update_table                          |
    -------------------------------------------------
    | table_name | ID_colname | ID  |  col_name     |
    -------------------------------------------------
    |   airplane |   air_ID   |  1  | tail_number   |
    |   airplane |   air_ID   |  2  | tail_number   |
    |    bus     |   bus_ID   | 2   | tag_number    |
    |    bus     |   bus_ID   | 3   | tag_number    |
    |    train   |   tr_ID    |  2  |designated_name|
    |    train   |   tr_ID    |  3  |designated_name|
    |Cruise_Ship |   sea_ID   |  1  |  hull_number  |
    |Cruise_Ship |   sea_ID   |  3  |  hull_number  |
    -------------------------------------------------

使用此表,我试图生成一个动态SQL以一次调用更新所有表


    SET @SQLString = N'UPDATE @table 
        SET  @value = '+ @empty +'
        where @key = @id';

    SET @ParmDefinition = N'@table nvarchar(max),
        @value nvarchar(max) ,
        @key nvarchar(max) ,
        @id int';

    DECLARE @table nvarchar(255)
    DECLARE @value nvarchar(255)
    DECLARE @key nvarchar(255)
    DECLARE @id int

    select @table = table_name, @id = ID, @key = ID_colname , @value = col_name from update_table

    EXECUTE sp_executesql
        @SQLString
        ,@ParmDefinition
        ,@table
        ,@value
        ,@key
        ,@id
        ;

但这不起作用,任何人都对如何改进此查询有一个想法?

这是一个引人注目的环境,开发人员不是执行代码的人,因此它需要客户证明。 该代码会隔夜运行,以免打扰白天的工作。

2 个答案:

答案 0 :(得分:3)

根据SQL Server documentation

如果SELECT语句返回多行,并且该变量引用一个非标量表达式,则该变量将设置为结果集最后一行中该表达式返回的值。

这意味着只为变量分配了最后一行的值。 因此,只有[Cruise_Ship]。[hull_number]将传递给sp_executesql,并且这是用脚本更新的唯一列。

要存储多个值,应使用表变量。

sp_executesql确实接受参数,并且用于构建动态查询。

但是,我认为您不能将表值变量作为参数传递。

已添加:检查this,了解如何将表值变量传递给sp_executesql。

这是您的代码出错的地方。

select @table = table_name, @id = ID, @key = ID_colname , @value = col_name from update_table

我知道这不太好,但是以下代码可以完成这项工作。 而且我建议无论您是否进行备份,都将其包装在TRANSACTION中。

DECLARE @empty NVARCHAR(10) 
SET @empty = 'NULL'

SELECT
    'UPDATE ' + s.name + '.' + t.name + ' 
    SET [' + c.name + '] =  ' + @empty + ' 
    WHERE LTRIM([' + c.name + ']) = ''''
    END;'
FROM sys.tables t
INNER JOIN sys.schemas s
    ON t.schema_id = s.schema_id
INNER JOIN sys.columns c
    ON t.object_id = c.object_id
ORDER BY
    s.name
   ,t.name
   ,c.column_id

答案 1 :(得分:0)

我同意亚当·亚姆(Adam Yam)的观点,我只是扩大了答案,以包括来自update_tables的所需数据。

这样,仅对表上的这8个条目执行SQL。

结果是示例中正确更新了4个表。

DECLARE @currentId INT 
SELECT @currentId = MIN(tabl.ID) from udpate_table tabl

DECLARE @sql NVARCHAR(MAX)

WHILE (1 = 1)
BEGIN  
   --- execute for the current pk
    BEGIN
        SELECT @sql =
        'UPDATE ' + s.name + '.' + t.name + ' 
        SET [' + c.name + '] = '''' 
           where  ' + s.name + '.' + t.name + '.' + tab.ID_colname + ' = ' + convert (varchar(20), tab.ID) + ' '
        FROM sys.tables t
        INNER JOIN sys.schemas s
            ON t.schema_id = s.schema_id
        INNER JOIN sys.columns c
            ON t.object_id = c.object_id
        JOIN update_table tab
            on t.name = tab.table_name
            and c.name = tab.col_name
            and tab.ID = @currentId
        ORDER BY
            tab.ID
           ,s.name
           ,t.name
           ,c.column_id
    END

    exec sp_executesql @sql

    -- select the next id to handle    
  SELECT TOP 1 @currentId = tabl.ID
  FROM update_table tabl
  WHERE tabl.ID > @currentId 
  ORDER BY tabl.ID

  IF @@ROWCOUNT = 0 BREAK;

END