Найти запись, ближайшую к данной дате для каждой группы - SQL - PullRequest
1 голос
/ 20 мая 2019

Я новичок в sql.Предположим, у нас есть такая таблица:

+-------+----------+-----------+
|userid | statusid |   date    |
+-------+----------+-----------+
| 1     |  1       | 2018-10-10| 
| 1     |  2       | 2018-10-12|
| 2     |  1       | 2018-09-25|
| 2     |  1       | 2018-10-01|
+-------+----------+-----------+

Мне нужно получить идентификатор состояния каждого идентификатора пользователя на дату, максимально приближенную к заданной.Скажи, что моя дата 2018-10-01.Как бы я это сделал?Я пробовал различные групповые и разделы, но ничего не работает.Может кто-нибудь, пожалуйста, помогите?

РЕДАКТИРОВАТЬ: мой дб это Амазонка красное смещение

Ответы [ 2 ]

2 голосов
/ 20 мая 2019

Вы можете использовать row_number() оконную аналитическую функцию с упорядоченным по абсолютному значению различием даты.

(Обратите внимание, что row_number() не работает для MySQL 8-, поэтому функция не используется, но функция abs() есть.)

Я не знаюt знаю ваш DBMS

Это решение для Oracle:

with tab(userid, statusid, "date") as
(
 select 1,1,date'2018-10-10' from dual union all
 select 1,2,date'2018-10-12' from dual union all
 select 2,1,date'2018-09-25' from dual union all
 select 2,1,date'2018-10-02' from dual
)
select tt.userid, tt.statusid, tt."date"
  from
(
select t.userid, t.statusid , t."date",
       row_number() over (partition by t.userid 
                          order by abs("date" - date'2018-10-01')) as rn
  from tab t
) tt
where tt.rn = 1

Демонстрация для Oracle

Это решение для SQL Server:

with tab([userid], [statusid], [date]) as
(
 select 1,1,'2018-10-10' union all
 select 1,2,'2018-10-12' union all
 select 2,1,'2018-09-25' union all
 select 2,1,'2018-10-02' 
)
select tt.[userid], tt.[statusid], tt.[date]
  from
(
select t.[userid], t.[statusid] , t.[date], 
       row_number() over (partition by t.[userid] 
                          order by abs(datediff(day,[date],'2018-10-01'))) as rn
  from tab t
) tt
where tt.rn = 1

Демонстрация для SQL Server

Решение для My SQL:

select tt.userid, tt.statusid, tt.date
  from
  (
   select t.userid, t.statusid , t.date,
          @rn := if(@iter = t.userid, @rn + 1, 1) as rn,
          @iter := t.userid, 
          abs(date - date'2018-10-01') as df
     from tab t
     join (select @iter := 0, @rn := 0) as q_iter
    order by t.userid, abs(date - date'2018-10-01') 
  ) tt
where tt.rn = 1

Демонстрация для My SQL

Это решение для PostGRES:

with tab(userid, statusid, date) as
(
 select 1,1,'2018-10-10' union all
 select 1,2,'2018-10-12' union all
 select 2,1,'2018-09-25' union all
 select 2,1,'2018-10-02' 
)
select tt.userid, tt.statusid, tt.date
  from
(
select t.userid, t.statusid , t.date, 
       row_number() over (partition by t.userid
                          order by abs(date::date-'2018-10-01'::date)) as rn
  from tab t
) tt
where tt.rn = 1

Демо для PostGRESql

0 голосов
/ 20 мая 2019

Обычно для этого типа проблемы вы хотите, чтобы дата была или раньше заданной даты.

Если это так:

select t.*
from t
where t.date = (select max(t2.date)
                from t t2
                where t2.userid = t.userid and t2.date <= '2018-10-01'
               );
...