Я пытаюсь подготовить запрос, чтобы найти первую строку из нескольких строк в запросе на соединение.
У меня есть таблица истории двух таблиц и новая таблица данных. В новой таблице данных всегда идет пара, но в некоторых случаях данные не поступают парами, поэтому, чтобы найти недостающие пары, я готовлю запрос.
Таблица пар выглядит следующим образом:
А таблица истории выглядит так:
Id Tnum Snum Rnum
------------------------
1 A1234 F1 0
2 A1234 N1 0
3 B1234 SP 2
4 B1234 FW 2
5 A1234 F1 1
6 A1234 N1 1
7 C1234 I1 0
8 C1234 I2 0
9 A1234 F1 2
10 A1234 N1 2
Новая таблица данных здесь:
Id Tnum Snum Rnum
-------------------------
1 A1234 F1 3
2 B1234 FW 3
3 C1234 I2 1
Согласно таблице пар, новые данные всегда должны быть в паре, что означает, что если Snum равен F1, тогда должна быть также запись для N1 для этого Tnum, а если Snum равен FW, то должна быть также запись для SP. В новой таблице мы видим, что записи не в парах, поэтому мне нужно найти недостающие пары.
Как мне проверить отсутствующие пары: проверьте предыдущую запись отсутствующей пары в таблице истории и возьмите последнюю запись Rnum этой недостающей пары. Например: в таблице данных у нас есть Snum = F1 для Tnum A1234. И для этого Tnum N1 не существует, поэтому мне нужно взять самую высокую запись Rnum N1 для этого Tnum из таблицы истории. Для Snum = F1 и Tnum A1234 запись об отсутствующей паре будет Snum = N1 для Tnum A1234 и Rnum = 2.
Ожидаемый результат отсутствующей пары в новой таблице данных должен выглядеть следующим образом:
History_Id Tnum Snum Rnum
--------------------------------
10 A1234 N1 2
3 B1234 SP 2
7 C1234 I1 0
Я подготовил запрос, как показано ниже, но он не дает ожидаемого результата. Он возвращает только 1 запись, но должен возвращать 3 записи, как и ожидалось.
WITH previous_latest_missing_pair as
(
SELECT *
FROM history_table
WHERE id IN (SELECT MAX(h.id)
FROM history_table h
JOIN new_data_table n ON n.Tnum = h.Tnum
WHERE h.Snum = CASE
WHEN n.Snum = 'SP' THEN 'FW'
WHEN n.Snum = 'FW' THEN 'SP'
WHEN n.Snum = 'F1' THEN 'N1'
WHEN n.Snum = 'N1' THEN 'F1'
WHEN n.Snum = 'I1' THEN 'I2'
WHEN n.Snum = 'I2' THEN 'I1'
END)
)
SELECT h.*
FROM history_table h
JOIN previous_latest_missing_pair p ON p.Tnum = h.Tnum
AND p.Snum = h.Snum
AND p.Rnum = h.Rnum
Вот скрипт с данными ОП и переформатированным запросом.
(1) Насколько я понимаю ваши данные, в каждой новой строке данных отсутствует соответствующая пара. Всегда ли так будет? (2) Для каждой строки, содержащей отсутствующую пару, хотите ли вы выбрать последнюю строку истории для этого отсутствующего парного значения Snum? (3) Что, если в новых данных присутствуют совпадающие пары (например, если был включен (4, 'C1234', 'I1', 1))? Следует ли игнорировать эти строки (действий не требуется)? (4) Почему вы используете выражение CASE, а не таблицу Pairs в своем запросе, чтобы сопоставить значение Snum с соответствующим парным значением
К вашему сведению, срок службы SQL Server 2008 истек более 5 лет назад; использование такой устаревшей и небезопасной версии SQL Server представляет собой серьезную проблему для вас, вашего бизнеса и ваших клиентов. Вам необходимо как можно скорее реализовать свой окончательный план обновления, разработанный более 6 лет назад.
Пожалуйста, прочтите: Пожалуйста, не загружайте изображения кода/данных/ошибок.
@TN 1. Да, так всегда бывает, потому что в новой таблице данных всегда появляются отсутствующие записи. 2. Да 3. В новой таблице данных никогда не будет совпадающей пары, поскольку в этой таблице всегда отсутствует запись о паре. 4. Я использую выражение CASE, поскольку парной таблицы нет и у меня нет разрешения на создание новой таблицы. Я упомянул здесь таблицу пар только для того, чтобы объяснить сценарий сопряжения.
@ThomA извините, я по ошибке добавил тег sql server 2008. Мы используем SQL-сервер 2019.


Что-то вроде этого?
with Missing2ndParts as (
select n.*, P.SecondPart as MissingPart
from new_data_table n
inner join
Pairs P
on n.snum=P.FirstPart
where not exists (select *
from new_data_table n2
where n2.tnum=n.tnum
and n2.snum=P.SecondPart)
)
select *
, (select max(h.rnum)
from history_table h
where h.tnum=m.tnum
and h.snum=m.MissingPart) as RNum
from Missing2ndParts m
Спасибо @tinazmu, у меня это сработало
После того как вы сопоставите новое значение данных Snum с парным значением Snum, вы можете использовать шаблон CROSS APPLY (SELECT TOP 1 ... ORDER BY ...), чтобы выбрать наиболее подходящую строку истории. В этом случае наилучшее значение определяется как строка с наибольшим значением Rnum.
with pairs as (
select v.*
from (
values
('F1', 'N1'), ('N1', 'F1'),
('SP', 'FW'), ('FW', 'SP'),
('I1', 'I2'), ('I2', 'I1')
) v(First, Second)
)
select h1.*
from new_data_table n
join pairs p on p.First = n.Snum
cross apply (
select top 1 h.*
from history_table h
where h.Tnum = n.Tnum
and h.Snum = p.Second
order by h.Rnum desc
) h1
Вместо выражения CASE я решил инкапсулировать парные данные в CTE, чтобы основная логика запроса была более краткой. Вы также можете создать пары из существующих исторических данных:
with pairs as (
select distinct h1.Snum AS First, h2.Snum AS Second
from history_table h1
join history_table h2
on h2.Tnum = h1.Tnum
and h2.Snum <> h1.Snum
)
select ...
Другой альтернативой является загрузка парных данных в переменную @table или таблицу #temp. Для этого не требуется доступ к созданию таблицы в текущей базе данных.
Результаты:
См. эту db<>fiddle для демонстрации.
Спасибо @TN, ваше решение мне тоже помогло без создания таблицы сопряжения.
Пожалуйста, не используйте изображения, используйте уценку таблицы для данных.