Выберите верхнюю 1 строку из нескольких строк во внутреннем соединении

Я пытаюсь подготовить запрос, чтобы найти первую строку из нескольких строк в запросе на соединение.

У меня есть таблица истории двух таблиц и новая таблица данных. В новой таблице данных всегда идет пара, но в некоторых случаях данные не поступают парами, поэтому, чтобы найти недостающие пары, я готовлю запрос.

Таблица пар выглядит следующим образом:

А таблица истории выглядит так:

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

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

Dale K 02.09.2024 21:59

Вот скрипт с данными ОП и переформатированным запросом.

T N 02.09.2024 22:59

(1) Насколько я понимаю ваши данные, в каждой новой строке данных отсутствует соответствующая пара. Всегда ли так будет? (2) Для каждой строки, содержащей отсутствующую пару, хотите ли вы выбрать последнюю строку истории для этого отсутствующего парного значения Snum? (3) Что, если в новых данных присутствуют совпадающие пары (например, если был включен (4, 'C1234', 'I1', 1))? Следует ли игнорировать эти строки (действий не требуется)? (4) Почему вы используете выражение CASE, а не таблицу Pairs в своем запросе, чтобы сопоставить значение Snum с соответствующим парным значением

T N 02.09.2024 23:08

К вашему сведению, срок службы SQL Server 2008 истек более 5 лет назад; использование такой устаревшей и небезопасной версии SQL Server представляет собой серьезную проблему для вас, вашего бизнеса и ваших клиентов. Вам необходимо как можно скорее реализовать свой окончательный план обновления, разработанный более 6 лет назад.

Thom A 03.09.2024 00:27

Пожалуйста, прочтите: Пожалуйста, не загружайте изображения кода/данных/ошибок.

MatBailie 03.09.2024 00:57

@TN 1. Да, так всегда бывает, потому что в новой таблице данных всегда появляются отсутствующие записи. 2. Да 3. В новой таблице данных никогда не будет совпадающей пары, поскольку в этой таблице всегда отсутствует запись о паре. 4. Я использую выражение CASE, поскольку парной таблицы нет и у меня нет разрешения на создание новой таблицы. Я упомянул здесь таблицу пар только для того, чтобы объяснить сценарий сопряжения.

Rahul 03.09.2024 08:56

@ThomA извините, я по ошибке добавил тег sql server 2008. Мы используем SQL-сервер 2019.

Rahul 03.09.2024 08:57
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
7
121
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Что-то вроде этого?

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, у меня это сработало

Rahul 06.09.2024 16:31
Ответ принят как подходящий

После того как вы сопоставите новое значение данных 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. Для этого не требуется доступ к созданию таблицы в текущей базе данных.

Результаты:

Идентификатор Тнум Снум Рнум 10 А1234 N1 2 3 Б1234 СП 2 7 C1234 я1 0

См. эту db<>fiddle для демонстрации.

Спасибо @TN, ваше решение мне тоже помогло без создания таблицы сопряжения.

Rahul 06.09.2024 16:32

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