У меня есть таблица со структурой, все не обнуляемой. таблица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
Кстати, я бы сделал это по-другому, если бы существующие данные могли иметь значения NULL в столбце 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)
Пожалуйста, смотрите обновления к вопросу. Я не получаю желаемого результата.
Могут быть более быстрые способы, но я бы выбрал решение 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), это гарантирует, что мы не будем обновляться из любой другой случайной строки.
Конечный результат:
Это работало с данными, которые я предоставил в вопросе. Спасибо.
Пожалуйста, предоставьте минимально воспроизводимый пример с данными образца и желаемыми результатами.