我可以根据包含列中 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
;
但这不起作用,有人知道如何改进此查询吗?
这是一个高调的环境,开发人员不是执行代码的人,因此需要客户证明。
该代码 运行 过夜,以免影响白天的操作。
如果 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
当前正在处理一个项目,我必须更新 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
;
但这不起作用,有人知道如何改进此查询吗?
这是一个高调的环境,开发人员不是执行代码的人,因此需要客户证明。 该代码 运行 过夜,以免影响白天的操作。
如果 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