Oracle SQL: возвращает запись с самой ранней и последней датой для каждого идентификатора и названия продукта

#sql #oracle #oracle-sqldeveloper

#sql #Oracle #oracle-sqldeveloper

Вопрос:

У меня есть таблица, которая выглядит следующим образом:

 ID  Product DATE
1   A       1
1   B       1
1   A       2
1   B       2
1   A       3
1   B       3
2   A       1
2   B       1
2   A       2
2   B       2
2   A       3
2   B       3
.   .
.   .
 

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

 ID  Product DATE
1   A       1
1   B       1
1   A       3
1   B       3

2   A       1
2   B       1
2   A       3
2   B       3

 

При этом мне удалось получить только первую дату:

 
select *
from (
    select t.*, rank() over(partition by t.ID order by t.DATE) rn
    from t
) t
where rn = 1

 

Это правильный путь? И как я могу также добавить последнюю дату?

Комментарии:

1. Вы получили 4 ответа и приняли единственный неправильный!

Ответ №1:

Я бы использовал оконные функции:

 select t.*
from (select t.*,
             min(date) over (partition by id) as min_date,
             max(date) over (partition by id) as max_date
      from t
     ) t
where date in (min_date, max_date);
 

Альтернативный метод заключается в использовании коррелированных подзапросов:

 select t.*
from t
where t.date = (select min(t.date) 
                from t t2 
                where t2.id = t.id
               ) or
      t.date = (select max(t.date) 
                from t t2 
                where t2.id = t.id
               ) ;
  
 

Комментарии:

1. Спасибо, я не был знаком с функциями окна, таким образом, он работал намного лучше и быстрее, чем я себе представлял.

Ответ №2:

Что-то вроде этого? Используйте MIN и MAX агрегируйте функции. Строки # 1 — 13 представляют образцы данных (вы их не вводите); необходимый запрос начинается со строки # 14.

 SQL> with test (id, product, datum) as
  2    (select 1, 'A', 1 from dual union all
  3     select 1, 'B', 1 from dual union all
  4     select 1, 'A', 2 from dual union all
  5     select 1, 'B', 2 from dual union all
  6     select 1, 'A', 3 from dual union all
  7     select 1, 'B', 3 from dual union all
  8     --
  9     select 2, 'A', 1 from dual union all
 10     select 2, 'B', 1 from dual union all
 11     select 2, 'A', 2 from dual union all
 12     select 2, 'B', 2 from dual
 13    )
 14  select id, product,
 15    min(datum) datum
 16    from test
 17    group by id, product
 18  --
 19  union
 20  --
 21  select id, product,
 22    max(datum) datum
 23    from test
 24    group by id, product
 25  order by id, datum, product;

        ID PRODUCT         DATUM
---------- ---------- ----------
         1 A                   1
         1 B                   1
         1 A                   3
         1 B                   3
         2 A                   1
         2 B                   1
         2 A                   2
         2 B                   2

8 rows selected.

SQL>
 

Комментарии:

1. Спасибо, это работает для этой таблицы, но если в таблице больше столбцов, которые я хочу включить в выходные данные, это усложняется, и функции окна, предложенные в другом ответе, кажутся проще и быстрее.

Ответ №3:

Вы можете сделать это с RANK() помощью (или ROW_NUMBER() , если нет повторяющихся дат) оконной функции:

 SELECT t.ID, t.Product, t."DATE"
FROM (
  SELECT t.*,
         RANK() OVER (PARTITION BY t.ID, t.Product ORDER BY t."DATE") rn_min,
         RANK() OVER (PARTITION BY t.ID, t.Product ORDER BY t."DATE" DESC) rn_max
  FROM tablename t
) t
WHERE 1 IN (rn_min, rn_max)
ORDER BY t.ID, t."DATE", t.Product
 

Смотрите демонстрацию.
Результаты:

 > ID | PRODUCT | DATE
> -: | :------ | ---:
>  1 | A       |    1
>  1 | B       |    1
>  1 | A       |    3
>  1 | B       |    3
>  2 | A       |    1
>  2 | B       |    1
>  2 | A       |    3
>  2 | B       |    3
 

Ответ №4:

Еще одним вариантом является использование first_value last_value аналитической функции and следующим образом:

 Select id, product, date from
(Select t.*,
       First_value(date) over (partition by id, product order by date) as fdate,
       Last_value(date) over (partition by id, product order by date) as ldate
  From your_table t)
Where date = fdate or date = ldate;