【问题标题】:How to Update a column by getting the column name dynamically from another table in sql server如何通过从sql server中的另一个表动态获取列名来更新列
【发布时间】:2017-05-17 11:49:07
【问题描述】:
 create table #tableA
(
    Id int,
    ColumnName1 nvarchar(50),
    ColumnName2 nvarchar(50),
    ColumnName3 nvarchar(50),
    ColumnName4 nvarchar(50) 
) 
 create table #tableB
(
    Id int,
    UpdateColumn nvarchar(50),
    UpdateValue nvarchar(50)
) 

Insert Into #tableA values (1,'Val1','Val2','Val3','Val4')
Insert Into #tableA values (2,'Val1','Val2','Val3','Val4')
Insert Into #tableA values (3,'Val1','Val2','Val3','Val4')
Insert Into #tableA values (4,'Val1','Val2','Val3','Val4')

Insert Into #tableB values (1,'ColumnName4','Column4Value')
Insert Into #tableB values (2,'ColumnName1','Column1Value') 

declare @Sql nvarchar(max)
set @Sql='Update a set {The column should be the value from b.UpdateColumn}={b.UpdateValue} from #tableA a join #tableB b on a.Id=b.Id'

print @Sql
exec sp_executesql @Sql 

我需要根据#tableB 值动态更新#tableA 列。

我尝试通过连接 join 子句中的值来构建动态 sql,但没有任何效果。

请建议..提前谢谢...

【问题讨论】:

  • 循环遍历#tableb 并形成一个动态更新脚本可以,但使用循环由您自行决定。
  • 我之前做过,工作正常,但性能不会那么好......

标签: sql sql-server dynamic-sql


【解决方案1】:

我会使用动态 SQL 来执行此操作(尽管您可以)。假设每个 id 只有一个更新,请使用 case

update a
    set ColumnName1 = (case when b.UpdateColumn = 'ColumnName1' then b.UpdateValue else ColumnName1 end),
        ColumnName2 = (case when b.UpdateColumn = 'ColumnName2' then b.UpdateValue else ColumnName2 end),
        ColumnName3 = (case when b.UpdateColumn = 'ColumnName3' then b.UpdateValue else ColumnName3 end),
        ColumnName4 = (case when b.UpdateColumn = 'ColumnName4' then b.UpdateValue else ColumnName4 end)
    from #tableA a join
         #tableB b
         on a.id = b.id;

【讨论】:

  • 我可以这样做,但是实际的表有更多的列,每次它也会更新不必要的列,我每次都必须更新大约 10000 行。该查询的性能如何?
  • @KousiK 。 . .更新的开销基于更新。多列会增加一点开销,但差异甚至可能不明显。
【解决方案2】:

如果您想从您的引用表中生成一个update 语句,您可以使用stufffor xml 将您的所有操作连接成一个字符串值:

if object_id('tempdb..#tableA') is not null
drop table #tableA;
if object_id('tempdb..#tableB') is not null
drop table #tableB;

create table #tableA
(
    Id int,
    ColumnName1 nvarchar(50),
    ColumnName2 nvarchar(50),
    ColumnName3 nvarchar(50),
    ColumnName4 nvarchar(50) 
);
insert Into #tableA values (1,'Val1','Val2','Val3','Val4'),(2,'Val1','Val2','Val3','Val4'),(3,'Val1','Val2','Val3','Val4'),(4,'Val1','Val2','Val3','Val4');

create table #tableB
(
    Id int,
    UpdateColumn nvarchar(50),
    UpdateValue nvarchar(50)
);
insert Into #tableB values (1,'ColumnName4','Column4Value'),(1,'ColumnName3','Column3Value'),(2,'ColumnName1','Column1Value');

declare @sql nvarchar(max);
with c as
(
select distinct id
                ,stuff((select char(10) + '   ,' + UpdateColumn + ' = ''' + UpdateValue + ''''
                        from #tableB
                        where id = b.id
                        for xml path('')
                        ),1,5,'') as Cols
from #tableB b
)
select @sql = stuff((select ';update #tableA set'
                            + char(10)
                            + '    ' + c.Cols
                            + char(10)
                            + 'where id = ' + cast(c.id as nvarchar(10))
                            + char(10)
                            + char(10)
                from c
                for xml path('')
                ),1,1,'')

print @sql;
exec sp_executesql @sql;

输出并执行以下脚本:

update #tableA set
    ColumnName4 = 'Column4Value'
   ,ColumnName3 = 'Column3Value'
where id = 1

;update #tableA set
    ColumnName1 = 'Column1Value'
where id = 2

【讨论】:

  • 它的工作,但加入条件不保持。它更新所有行,但我需要使用该连接子句过滤它..
  • @KousiK 啊,对不起,我误解了你的要求。我已经更新了脚本。
  • ,ColumnName4 逗号是导致问题的原因,将其从 + ' ,' + b.UpdateColumn 中删除,它起作用了,但如果 nvarchar(max) 长度有 10000 行,请告诉我一件事将超过那将是什么解决方案?
  • @KousiK 是的,正如我所说,那个版本不工作。我再次更新了它,你应该得到你需要的脚本。关于超过 nvarchar(max) 变量的限制,它可以容纳超过十亿个字符,所以我认为这对你来说不是问题。
  • @KousiK 那么你在应用我的脚本时做错了,因为在我的脚本中任何地方都没有名为a 的对象,它在我的机器上按原样运行没有问题。
【解决方案3】:

使用触发器。

CREATE TRIGGER triggerName ON [dbo].[tableB] 
AFTER Update
AS
Begin
--Update tableA here
End

现在每次 tableB 更新时都会调用此触发器,您可以在其中使用任何条件。

【讨论】:

  • 不能使用触发器这些都是在存储过程中动态生成的临时表。
【解决方案4】:
drop table if exists #tableA;
drop table if exists #tableB;

create table #tableA
(
    Id int,
    ColumnName1 nvarchar(50),
    ColumnName2 nvarchar(50),
    ColumnName3 nvarchar(50),
    ColumnName4 nvarchar(50) 
) 
 create table #tableB
(
    Id int,
    UpdateColumn nvarchar(50),
    UpdateValue nvarchar(50)
) 

Insert Into #tableA values (1,'Val1','Val2','Val3','Val4')
Insert Into #tableA values (2,'Val1','Val2','Val3','Val4')
Insert Into #tableA values (3,'Val1','Val2','Val3','Val4')
Insert Into #tableA values (4,'Val1','Val2','Val3','Val4')

Insert Into #tableB values (1,'ColumnName4','Column4Value')
Insert Into #tableB values (2,'ColumnName1','Column1Value') 

declare @Sql nvarchar(max)

select
    @SQL = string_agg('update a
    set
        ' + b.UpdateColumn +  ' = ''' + b.UpdateValue + '''
    from #tableA a', ';')
from #tableB b

print @Sql
exec sp_executesql @Sql

select
*
from #tableA

【讨论】:

  • "string_agg" 这是一个内置函数吗?
  • @KousiK,是的,来自 SQL Server 2017。如果您不在 SQL Server 2017 上(还在等什么 :-)),您可以使用 Micsosoft.StringUtilities CLR 创建 Concatenate 聚合函数. (docs.microsoft.com/en-us/sql/t-sql/statements/…)
猜你喜欢
  • 2022-01-23
  • 2014-06-30
  • 2016-07-26
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
相关资源
最近更新 更多