SQL Oracle: найдите все записи, доступные с одного и того же «ID», затем выберите только те, которые имеют определенное состояние активности

Я не могу найти, как написать запрос, который:

Учитывая эти данные с X (много) записей:

ИДЕНТИФИКАТОР Активность Дата 1111 С 10.04.2023 2222 М 11.04.2023 3333 С 10.04.2023 1111 М 11.04.2023 3333 М 11.04.2023 4444 С 11.04.2023 5555 М 10.04.2023 5555 М 11.04.2023

возвращает только самые последние (по дате) записи с состоянием активности: "M", у которых нет предыдущей записи (на другую дату, из прошлого или того же дня) с состоянием "S".

Как видите, идентификаторы:

  • 1111 --> Записи X с действиями S и M
  • 2222 --> Только одна запись с активностью M
  • 3333 --> X записей с действиями S и M
  • 4444 --> Только одна запись с активностью S
  • 5555 --> Есть x записей только с M действиями

Итак, я ожидаю увидеть результаты:

  • 2222 от 11.04.2023
  • 5555 от 11.04.2023

Короче говоря: я пытаюсь найти только записи с действиями «M» и без действий «S» и выбрать самую последнюю, используя дату.

Заранее спасибо, Лука.

Я пытался подумать о выборе отдельного идентификатора из таблицы, где Activity = 'M', но это не гарантирует мне, что для того же идентификатора не будет записей с Activity = 'S' в последние дни.

Я также пытался подумать о группе по идентификатору, но и здесь я не могу гарантировать, что выбранные записи не имеют предыдущей записи в другую дату с активностью «S» в прошлые дни.

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
0
55
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вот один вариант.

(Просто задайте формат даты, чтобы узнать, что к чему; из опубликованных вами данных неясно, например, 2023/04/10 может быть 10 апреля, а также 4 октября).

SQL> alter session set nls_date_format = 'yyyy/mm/dd';

Session altered.

Образец данных:

SQL> with test (id, activity, datum) as
  2    (select 1111, 'S', date '2023-04-10' from dual union all
  3     select 2222, 'M', date '2023-04-11' from dual union all
  4     select 3333, 'S', date '2023-04-10' from dual union all
  5     select 1111, 'M', date '2023-04-11' from dual union all
  6     select 3333, 'M', date '2023-04-11' from dual union all
  7     select 4444, 'S', date '2023-04-11' from dual union all
  8     select 5555, 'M', date '2023-04-10' from dual union all
  9     select 5555, 'M', date '2023-04-11' from dual
 10    )

Запрос начинается здесь; temp CTE сортирует все строки (для ID, у которых нет активности S) для каждого ID по datum в порядке убывания):

 11  temp as
 12    (select a.id, a.activity, a.datum,
 13       row_number() over (partition by a.id order by a.datum desc) rn
 14     from test a
 15     where not exists (select null
 16                       from test b
 17                       where b.id = a.id
 18                         and b.activity = 'S'
 19                      )
 20    )

Наконец, верните строки, получившие наивысший рейтинг:

 21  select id, datum
 22  from temp
 23  where rn = 1
 24  order by id;

        ID DATUM
---------- ----------
      2222 2023/04/11
      5555 2023/04/11

SQL>

Здравствуйте, Littlefoot, я попробовал ваш ответ, и все работает нормально, спасибо за вашу помощь! Я принял ответ @MT0, так как нашел его более полным и мне было легче его понять, но еще раз спасибо!

Volturno Luca 11.04.2023 14:27

При условии «М» < «С»

with mx as(
  SELECT tbl.*, MAX(Activity) over (partition by ID) ma, max(Dt) over (partition by ID) mdt
  FROM tbl
) 
select ID, Activity, Dt
from mx
where ma ='M' and dt = mdt

Здравствуйте @Serg, я нашел ваш ответ немного более сложным, чем предыдущий от других парней, также я не нашел способа заставить его работать на меня, я имею в виду, что он дает мне результаты, отличные от двух других ответов. , в любом случае я нашел решение, так что спасибо за попытку помочь мне

Volturno Luca 11.04.2023 14:32

@VolturnoLuca, у меня он возвращает те же две строки dbfiddle.uk/fu9Nc5AM

Serg 11.04.2023 15:20

Странно, когда я добавил больше фильтров, он не показал тех же результатов, что и два других ответа, не знаю, что я делал неправильно.

Volturno Luca 11.04.2023 20:22
Ответ принят как подходящий

Вы можете использовать аналитические функции, чтобы найти последнюю строку и проверить, было ли действие S ранее, чтобы вы могли запрашивать таблицу только один раз:

SELECT id, activity, dt
FROM   (
  SELECT t.*,
         ROW_NUMBER() OVER (PARTITION BY id ORDER BY dt DESC) AS rn,
         FIRST_VALUE(CASE activity WHEN 'S' THEN 'S' END IGNORE NULLS) OVER (
             PARTITION BY id ORDER BY dt DESC
             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
           ) AS has_activity_s
  FROM   table_name t
)
WHERE rn = 1
AND   has_activity_s IS NULL;

Что для примера данных:

CREATE TABLE table_name (id, activity, dt) AS
  SELECT 1111, 'S', DATE '2023-04-10' FROM DUAL UNION ALL
  SELECT 2222, 'M', DATE '2023-04-11' FROM DUAL UNION ALL
  SELECT 3333, 'S', DATE '2023-04-10' FROM DUAL UNION ALL
  SELECT 1111, 'M', DATE '2023-04-11' FROM DUAL UNION ALL
  SELECT 3333, 'M', DATE '2023-04-11' FROM DUAL UNION ALL
  SELECT 4444, 'S', DATE '2023-04-11' FROM DUAL UNION ALL
  SELECT 5555, 'M', DATE '2023-04-10' FROM DUAL UNION ALL
  SELECT 5555, 'M', DATE '2023-04-11' FROM DUAL;

Выходы:

ИДЕНТИФИКАТОР АКТИВНОСТЬ ДТ 2222 М 2023-04-11 00:00:00 5555 М 2023-04-11 00:00:00

рабочий пример

Здравствуйте @MT0, спасибо за помощь, я принял ваш ответ, так как считаю его наиболее полным среди других. Я пробовал это, и это работает для того, что мне нужно. Спасибо!

Volturno Luca 11.04.2023 14:25

Еще один:

select * from data d1
where d1.Activity = 'M' and not exists(
    select 1 from data d2
    where (d1.id = d2.id) and (
        (d2.dat <= d1.dat and d2.Activity = 'S')
        or (d2.dat > d1.dat and d2.Activity = 'M')
    )
)

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