Получить последнюю дату изменения в базе данных по значению

# #sql #google-bigquery

Вопрос:

Как можно получить — когда было последнее изменение (по дате) — в этой таблице:

ID Дата ценность
1 01.01.2021 0.0
1 02.01.2021 10.0
1 03.01.2021 15.0
1 04.01.2021 25.0
1 05.01.2021 25.0
1 06.01.2021 25.0

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

Повторное значение должно быть:

ID Дата ценность
1 04.01.2021 25.0

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

1. означает ли запись с «05.01.2021» и «06.01.2021», что произошло какое-то другое изменение, или ее следует игнорировать, потому что значение все еще остается прежним?

2. значение может быть только выше предыдущего или также ниже ?

3. Его следует игнорировать, потому что значение по-прежнему остается прежним

4. Значение может быть также ниже

5. вы можете использовать РАЗДЕЛ ПО столбцу значения, попробуйте здесь sqlshack.com/sql-partition-by-clause-overview

Ответ №1:

Попробуйте вот это:

 with mytable as (
  select 1 as id, date '2021-01-01' as date, 0 as value union all
  select 1, date '2021-01-02', 10 union all
  select 1, date '2021-01-03', 15 union all
  select 1, date '2021-01-04', 25 union all
  select 1, date '2021-01-05', 25 union all
  select 1, date '2021-01-06', 25
)
select id, array_agg(struct(date, value) order by last_change_date desc limit 1)[offset(0)].*
from (
  select *, if(value != lag(value) over (partition by id order by date), date, null) as last_change_date
  from mytable
)
group by id
 

введите описание изображения здесь

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

1. он хочет последнюю перемену! не первый

Ответ №2:

в этом сценарии я бы использовал два поля в своей базе данных «created_at и updated_at» с типом «отметка времени». Вы можете просто получить свои записи, используя поле OrderBy «updated_at».

Ответ №3:

посмотрите, что это дает вам:

  SELECT MAX(date) OVER (PARTITION BY(value)) AS lastChange 

 FROM Table

 WHERE id = 1
 
 

Ответ №4:

Следующий запрос и воспроизводимый пример на db-fiddle работает. Я также включил некоторые дополнительные записи тестов.

 CREATE TABLE my_data (
  `id` INTEGER,
  `date` date,
  `value` INTEGER
);

INSERT INTO my_data
  (`id`, `date`, `value`)
VALUES
  ('1', '01.01.2021', '0.0'),
  ('1', '02.01.2021', '10.0'),
  ('1', '03.01.2021', '15.0'),
  ('1', '04.01.2021', '25.0'),
  ('1', '05.01.2021', '25.0'),
  ('1', '06.01.2021', '25.0'),
  ('2', '05.01.2021', '25.0'),
  ('2', '06.01.2021', '23.0'),
  ('3', '03.01.2021', '15.0'),
  ('3', '04.01.2021', '25.0'),
  ('3', '05.01.2021', '17.0'),
  ('3', '06.01.2021', '17.0');
 

Запрос № 1

 SELECT 
    id,
    date,
    value
FROM (
    SELECT
        *,
        row_number() over (partition by id order by date desc)  as id_rank
    FROM (
        SELECT
            id,
            m1.date,
            m1.value,
            rank() over (partition by id,m1.value order by date asc)  as id_value_rank,
            CASE
                WHEN (m1.date = (max(m1.date) over (partition by id,m1.value ))) THEN 1
        ELSE 0
    END AS is_max_date_for_group,
    CASE
        WHEN (m1.date = (max(m1.date) over (partition by id ))) THEN 1
        ELSE 0
    END AS is_max_date_for_id                                 
from 
    my_data m1
) m2
WHERE (m2.is_max_date_for_group = m2.is_max_date_for_id and is_max_date_for_group <> 0 and id_value_rank=1) or (id_value_rank=1 and is_max_date_for_id=0)
) t
              where t.id_rank=1
                         order by id, date, value;
 
ID Дата ценность
1 04.01.2021 25
2 06.01.2021 23
3 05.01.2021 17

Просмотр на скрипке БД

Ответ №5:

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

 select id, min(date), value
from (select t.*,
             row_number() over (partition by id order by date desc) as seqnum,
             row_number() over (partition by id, value order by date desc) as seqnum_2
      from t
     ) t
where seqnum = seqnum_2
group by id;
 

Если вы используете lag() , я бы рекомендовал использовать qualify для повышения производительности:

 select t.*
from (select t.*
      from t
      qualify lag(value) over (partition by id order by date) <> value or
              lag(value) over (partition by id order by date) is null
     ) t
qualify row_number() over (partition by id order by date desc) = 1;
 

Примечание: Оба эти метода работают, если значение одинаково для всех строк. Другие методы могут не сработать в этой ситуации.