Обновить строки для общих и конкретных входных данных

У меня есть таблица со структурой, все не обнуляемой. таблица1: столбец1, столбец2, столбец3, столбец4

Данные Таблицы 1:

col1  col2  col3  col4
======================
kc11  kc21  kc31  100
kc11  kc21  kc32  125
kc11  kc22  kc31  150
kc11  kc22  kc32  105
kc11  kc22  kc33  106
kc11  kc22  kc34  107
kc11  kc22  kc35  108

Клиентское приложение отправляет обновление данных в формате JSON. Столбцы с 1 по 3 — это ключи со значением в столбце 4. Значения в столбце 3 могут быть нулевыми, что означает, что они применяются ко всем строкам, соответствующим только столбцам 1 и 2. Данные могут содержать определенные записи для столбца 3, которые необходимо применить вместо записи с нулевым значением. Я загружаю данные в @input с теми же именами столбцов.

@input загружен:

col1  col2  col3  col4
======================
kc11  kc21  null  160
kc11  kc22  null  175
kc11  kc22  kc32  125

Я пытаюсь придумать запрос, который будет обновлять таблицу1 из @input, но не знаю, как объединить таблицы, чтобы определенные записи col3 обновлялись из строк со значениями col3 в @input.

update table1
set col4=inp.col4
from table1 tbl1
inner join @input inp on tbl1.col1=inp.col1 and tbl1.col2=inp.col2 and ?

Могу ли я написать один запрос, чтобы получить результат ниже?

Таблица 1. Данные обновлены:

col1  col2  col3  col4
======================
kc11  kc21  kc31  160
kc11  kc21  kc32  160
kc11  kc22  kc31  175
kc11  kc22  kc32  125
kc11  kc22  kc33  175
kc11  kc22  kc34  175
kc11  kc22  kc35  175

Тест ниже не дал желаемого результата:

declare @table1 table
(
col1 varchar(10),
col2 varchar(10),
col3 varchar(10),
col4  varchar(10)
);

insert @table1 (col1,col2,col3,col4) values ('kc11','kc21','kc31','100');
insert @table1 (col1,col2,col3,col4) values ('kc11','kc21','kc32','125');
insert @table1 (col1,col2,col3,col4) values ('kc11','kc22','kc31','150');
insert @table1 (col1,col2,col3,col4) values ('kc11','kc22','kc32','105');
insert @table1 (col1,col2,col3,col4) values ('kc11','kc22','kc33','106');
insert @table1 (col1,col2,col3,col4) values ('kc11','kc22','kc34','107');
insert @table1 (col1,col2,col3,col4) values ('kc11','kc22','kc35','108');

declare @input table
(
col1 varchar(10),
col2 varchar(10),
col3 varchar(10),
col4  varchar(10)
);

insert @input (col1,col2,col3,col4) values ('kc11','kc21',  null,'160');
insert @input (col1,col2,col3,col4) values ('kc11','kc22',  null,'175');
insert @input (col1,col2,col3,col4) values ('kc11','kc22','kc32','125');

select * from @table1;
update @table1
set col4=inp.col4
from @table1 tbl1
inner join @input inp on tbl1.col1=inp.col1 and tbl1.col2=inp.col2 
    and (inp.col3 is null or inp.col3 = tbl1.col3)

select * from @table1;

Вывод запроса:

col1       col2       col3       col4
---------- ---------- ---------- ----------
kc11       kc21       kc31       100
kc11       kc21       kc32       125
kc11       kc22       kc31       150
kc11       kc22       kc32       105
kc11       kc22       kc33       106
kc11       kc22       kc34       107
kc11       kc22       kc35       108

Желаемый результат:

col1       col2       col3       col4
---------- ---------- ---------- ----------
kc11       kc21       kc31       160
kc11       kc21       kc32       160
kc11       kc22       kc31       175
kc11       kc22       kc32       175
kc11       kc22       kc33       175
kc11       kc22       kc34       175
kc11       kc22       kc35       175

Пожалуйста, предоставьте минимально воспроизводимый пример с данными образца и желаемыми результатами.

Dale K 20.06.2024 21:01

Кстати, я бы сделал это по-другому, если бы существующие данные могли иметь значения NULL в столбце 3, так что это было бы полезной информацией в вопросе.

Joel Coehoorn 20.06.2024 21:15

Обновлено с дополнительной информацией.

workvact 20.06.2024 21:32
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
3
63
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

and (inp.col3 IS NULL OR inp.col3 = tb1.Col3)

Если вы знаете, что существующие данные table1 не будут иметь NULL в col3, я бы сделал это следующим образом:

update table1
set col4=inp.col4
from table1 tbl1
inner join @input inp on tbl1.col1=inp.col1 and tbl1.col2=inp.col2 
    and tbl1.col3 = coalesce(inp.col3, tbl1.col3)

Мне он нравится больше, чем альтернативы с OR, потому что он дает вам, по крайней мере, возможность использовать индекс со столбцом, но вы должны быть уверены, что существующие данные не будут NULL.

Если существующий столбец может иметь значение NULL, я бы сделал это, поскольку он более точно соответствует моему первому варианту:

update table1
set col4=inp.col4
from table1 tbl1
inner join @input inp on tbl1.col1=inp.col1 and tbl1.col2=inp.col2 
    and coalesce(tbl1.col3,'') = coalesce(inp.col3, tbl1.col3,'')

Однако coalesce() с обеих сторон означает, что индексы с col3 не помогут.

Или я бы сделал это, и именно это вы чаще всего увидите в других местах в Интернете:

update table1
set col4=inp.col4
from table1 tbl1
inner join @input inp on tbl1.col1=inp.col1 and tbl1.col2=inp.col2 
    and (inp.col3 is null or inp.col3 = tbl1.col3)

Пожалуйста, смотрите обновления к вопросу. Я не получаю желаемого результата.

workvact 20.06.2024 22:39
Ответ принят как подходящий

Могут быть более быстрые способы, но я бы выбрал решение CROSS APPLY TOP 1, что-то вроде:

SELECT  *, col4 AS col4_old
INTO #data
FROM
(
    VALUES  (N'kc11', N'kc21', N'kc31', 100)
    ,   (N'kc11', N'kc21', N'kc32', 125)
    ,   (N'kc11', N'kc22', N'kc31', 150)
    ,   (N'kc11', N'kc22', N'kc32', 105)
    ,   (N'kc11', N'kc22', N'kc33', 106)
    ,   (N'kc11', N'kc22', N'kc34', 107)
    ,   (N'kc11', N'kc22', N'kc35', 108)
) t (col1,col2,col3,col4)


UPDATE  t
SET col4 = y.col4
FROM    #data t
CROSS APPLY (
    SELECT  TOP 1 *
    FROM    (
        VALUES  (N'kc11', N'kc21', NULL, 160)
        ,   (N'kc11', N'kc22', NULL, 175)
        ,   (N'kc11', N'kc22', N'kc32', 125)
        ) n (col1,col2,col3,col4)
    WHERE   n.col1 = t.col1
    AND n.col2 = t.col2
    AND (n.col3 IS NULL OR n.col3 = t.col3) -- Sanity check
    ORDER BY CASE 
            WHEN n.col3 = t.col3 THEN 0 -- Exact match
            ELSE 1 -- col3 is NULL
        END
    ) y

По сути, я пытаюсь найти лучшее совпадение, беря ТОП-1, упорядоченный по точному совпадению, а затем нулевое значение.

Чтобы избежать обновления слишком большого количества строк, я добавил AND (n.col3 IS NULL OR n.col3 = t.col3), это гарантирует, что мы не будем обновляться из любой другой случайной строки.

Конечный результат:

столбец 1 столбец 2 столбец 3 столбец 4 col4_old кс11 кс21 кс31 160 100 кс11 кс21 кс32 160 125 кс11 кс22 кс31 175 150 кс11 кс22 кс32 125 105 кс11 кс22 кс33 175 106 кс11 кс22 кс34 175 107 кс11 кс22 кс35 175 108

Это работало с данными, которые я предоставил в вопросе. Спасибо.

workvact 20.06.2024 23:25

Другие вопросы по теме