根据另一个表中的值更新一个表中的变量列

时间:2020-01-16 14:49:36

标签: sql sql-server tsql sql-update

我有一个奇怪的用例。

假设我有一个简单的表Persons

USE TestDB
Go

CREATE TABLE Persons (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255),
    City varchar(255)
)

INSERT INTO Persons (PersonID, LastName, FirstName, City) 
VALUES
(1, 'Smith', 'John', 'New York'),
(2, 'Doe', 'Jane', 'Los Angeles'),
(3, 'Sixpack', 'Joe', 'Chicago')

我还有一个Overrides表,用于指定如何更改此表:

CREATE TABLE Overrides (
    PersonID int,
    ColumnName varchar(255),
    OverrideValue varchar(255)
)

INSERT INTO Overrides (PersonID, ColumnName, OverrideValue)
VALUES
(2, 'City', 'CHANGED CITY'),
(1, 'FirstName', 'CHANGED FIRSTNAME'),
(3, 'LastName', 'CHANGED LASTNAME')
  • PersonID指定唯一行
  • ColumnName告诉我该行的哪一列需要修改
  • OverrideValue告诉我应该在该行中输入什么值

我想创建一个将Overrides表中的替代应用于Persons表的过程。在上述情况下,Persons将从其原始状态开始:

PersonID        LastName        FirstName       City
-----------     -----------     -----------     -----------
1               Smith           John            New York
2               Doe             Jane            Los Angeles
3               Sixpack         Joe             Chicago

进入以下状态:

PersonID        LastName         FirstName         City
-----------     -----------      -----------       -----------
1               Smith            CHANGED FIRSTNAME New York
2               Doe              Jane              CHANGED CITY
3               CHANGED LASTNAME Joe               Chicago

我可以根据每行在Overrides表和UPDATE上进行非常丑陋的循环,但是我希望找到一种更优雅的方法来解决这种非优雅的情况。

2 个答案:

答案 0 :(得分:2)

考虑以下UPDATE ... JOIN ...语法:

update p
set 
    p.lastName  = case when o.columnName = 'LastName'  then o.overrideValue else p.lastName end,
    p.firstName = case when o.columnName = 'FirstName' then o.overrideValue else p.firstName end,
    p.city      = case when o.columnName = 'City'      then o.overrideValue else p.city end
from persons p 
inner join overrides o on  o.personID = p.personID

这是通过将personID上的两个表联接起来,然后使用case表达式来更新适当的列而起作用的;对于每个加入的记录,只会发生3个条件分配之一。

Demo on DB Fiddle

PersonID | LastName         | FirstName         | City        
-------: | :--------------- | :---------------- | :-----------
       1 | Smith            | CHANGED FIRSTNAME | New York    
       2 | Doe              | Jane              | CHANGED CITY
       3 | CHANGED LASTNAME | Joe               | Chicago     

答案 1 :(得分:1)

这有点复杂。如果您没有太多的列,那么多个left join可能是最简单的解决方案:

update p
    set firstname = coalesce(ofn.overridevalue, p.firstname),
        lastname = coalesce(ofn.overridevalue, p.lastname),
        city = coalesce(ofn.overridevalue, p.city)        
from persons p left join
     overrides ofn
     on p.personid = ofn.person_id and ofn.columnname = 'firstname' left join
     overrides oln
     on p.personid = oln.person_id and oln.columnname = 'lastname' left join
     overrides oc
     on p.personid = c.person_id and oc.columnname = 'city' 
where ofn.personid is not null or oln.personid is not null or oc.personid is not null;

如果您有很多列,那么预聚合可能是最好的解决方案:

update p
    set firstname = coalesce(ofn.firstname, p.firstname),
        lastname = coalesce(ofn.lastname, p.lastname),
        city = coalesce(ofn.city, p.city)        
from persons p left join
     (select o.personid,
             max(case when columnname = 'firstname' then overridevalue end) as firstname,
             max(case when columnname = 'lastname' then overridevalue end) as lastname,
             max(case when columnname = 'city' then overridevalue end) as city
     from overrides o
     group by o.personid
    ) o
    on o.personid = p.personid;