我可以根据包含列中 table 名称的另一个 table 更新许多 table 吗?

Can I update many tables based on another table containing the table names on a column?

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

主要想法是创建一个table来保存回滚数据。但我试图避免创建 85 tables.

我举个小例子:

有 4 tables


   ------------------------------------
   |            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 |                 |
   ------------------------------------

所以我用数据

创建了一个临时文件 table

    -------------------------------------------------
    |         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  |
    -------------------------------------------------

有了这个 table 我试图生成一个动态的 SQL 以通过一个调用 table 更新所有 tables


    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
        ;

但这不起作用,有人知道如何改进此查询吗?

这是一个高调的环境,开发人员不是执行代码的人,因此需要客户证明。 该代码 运行 过夜,以免影响白天的操作。

根据SQL Server documentation

如果 SELECT 语句 returns 多行且变量引用 non-scalar 表达式,则变量设置为最后一行中表达式返回的值结果集。

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

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

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

但是我认为您不能将 table-valued 变量作为参数传递。

添加:检查 this 如何将 table-valued 变量传递给 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

我同意 Adam Yam 的观点,我只是扩展了答案以包含来自 update_tables 的所需数据。

这样 SQL 的执行只会发生在 table 上的那 8 个条目。

结果是正确更新示例中的 4 tables。

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