Как выбрать максимальную / отличную запись в MySQL, используя столбец delete_at - PullRequest
0 голосов
/ 27 сентября 2019

Я пытаюсь выбрать отдельные строки по следующим двум правилам:

  • Если его дата delete_at равна нулю, то это самая последняя запись, выберите ее
  • Если этосамая поздняя дата удаленных_откатов (и нет записи с NULL), это также самая последняя запись, выберите ее

Рассмотрите эту таблицу:

enter image description here

Результат, который я ищу, будет:

enter image description here

Я использую MySQL mariaDB v10.1.33, которая делаетне все функции, которыми я пользуюсь.

NULL игнорировался, поэтому я использую coalesce(fc.deleted_at, CURRENT_TIMESTAMP()), чтобы сделать его самой последней датой.Таким образом, я могу использовать функцию max (), чтобы выбрать его.Однако когда я использую это, это не соответствует данным в строках!то есть это:

SELECT max(coalesce(fc.deleted_at, CURRENT_TIMESTAMP())), folder_id, code 
FROM folder_code fc 
WHERE fc.folder_id = 5683 

возвращает:

enter image description here

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

SELECT max(coalesce(fc.deleted_at, CURRENT_TIMESTAMP())) as maxdeleteddate, fc.folder_id, fc.code
FROM folder_code fc
WHERE fc.folder_id = 5683
GROUP BY fc.folder_id 
ORDER BY maxdeleteddate desc

Как мне достичь желаемого результата?

Спасибо

Ответы [ 4 ]

1 голос
/ 27 сентября 2019

Вот как я бы это сделал:

SELECT f1.*
FROM folder f1
    INNER JOIN (
        SELECT folder_id,
            NULLIF(MAX(IF(deleted_at IS NULL,NOW(),deleted_at)),NOW()) AS deleted_at
        FROM folder
        GROUP BY folder_id
    ) f2 ON f2.folder_id = f1.folder_id AND f2.deleted_at <=> f1.deleted_at

А вот скрипка: https://www.db -fiddle.com / f / wzCYktpavBNnJu2uejPpe9 / 1

Идея состоит в том, чтобы получить groupwise-max, а затем объединить вашу таблицу против себя.Если вы просто группируете строки, вы не гарантированно получите правильные значения для неагрегированных столбцов.

Существует также прием со столбцом deleted_at, с использованием NOW(), если он нулевой, затем с использованием NULLIF(), чтобы установить для него NULL для объединения.

Этот подход также выигрывает от того факта, что он потенциально использует индексы, если они существуют.

0 голосов
/ 27 сентября 2019

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

select *
from folder_code fc
where not exists
(
  select *
  from folder_code fc2
  where fc2.folder_id = fc.folder_id
  and coalesce(fc2.deleted_at, date '9999-12-31') > coalesce(fc.deleted_at, date '9999-12-31')
);
0 голосов
/ 27 сентября 2019

Вы можете попробовать ниже - используя коррелированный подзапрос

DEMO

select * from t1 a 
    where coalesce(deleted_at,CURRENT_TIMESTAMP()) =
     (select max(coalesce(deleted_at,CURRENT_TIMESTAMP())) from t1 a1 where a.folder_id=a1.folder_id)

ВЫХОД:

older_id    code    deleted_at
5333        12VA1   2019-09-27
5683        12SR1-X 
0 голосов
/ 27 сентября 2019

Если вы используете MySQL 8+, тогда вы можете использовать ROW_NUMBER здесь:

WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY folder_id
                                 ORDER BY -ISNULL(deleted_at), deleted_at DESC) rn
    FROM folder_code
)

SELECT folder_id, code, deleted_at
FROM cte
WHERE rn = 1;

enter image description here

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

Предложение ORDER BY, используемое при вызове ROW_NUMBER, помещает все записи, имеющие дату удаления NULL после даты записи, для каждой группы folder_idзаписей.Затем на втором уровне сортировки размещаются более свежие записи о дате удаления.Это означает, что для этих папок есть запись NULL, она будет отображаться первой, в противном случае самая последняя запись будет отображаться первой.

Вот решение старой школы, которое также может работать:

SELECT f1.folder_id, f1.code, f1.deleted_at
FROM folder_code f1
INNER JOIN
(
    SELECT folder_id,
        CASE WHEN COUNT(*) = COUNT(deleted_at)
             THEN MAX(deleted_at) END AS max_deleted_at
    FROM folder_code
    GROUP BY folder_id
) f2
    ON f1.folder_id = f2.folder_id AND
       (f1.deleted_at = f2.max_deleted_at OR
           (f1.deleted_at IS NULL AND f2.max_deleted_at IS NULL));

Демо

...