Обновить столбец переменной в одной таблице на основе значения в другой - PullRequest
1 голос
/ 16 января 2020

У меня несколько странный вариант использования.

Допустим, у меня есть простая таблица 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 будет go из исходного состояния:

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

Я мог бы сделать очень уродливым l oop через таблицу Overrides и UPDATE для каждой строки, но я надеялся найти более элегантный подход для этой не элегантной ситуации.

Ответы [ 2 ]

2 голосов
/ 16 января 2020

Рассмотрим следующий синтаксис 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 условных присвоений.

Демонстрация на DB Fiddle :

PersonID | LastName         | FirstName         | City        
-------: | :--------------- | :---------------- | :-----------
       1 | Smith            | CHANGED FIRSTNAME | New York    
       2 | Doe              | Jane              | CHANGED CITY
       3 | CHANGED LASTNAME | Joe               | Chicago     
1 голос
/ 16 января 2020

Это немного сложно. Если у вас не слишком много столбцов, то простое решение может быть несколько left join s:

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