SQL Производительность при выборе первой / последней строки для каждого пользователя в большей таблице данных - PullRequest
2 голосов
/ 14 января 2020

Я прочитал довольно много постов с наибольшим числом групп, но, похоже, все еще не нашел хорошего решения с точки зрения производительности. Я использую 10.1.43-MariaDB.

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

user_id    data          date        
4567          109          28/06/2019 11:04:45        
4252          309          18/06/2019 11:04:45      
4567          77          18/02/2019 11:04:45        
7893          1123          22/06/2019 11:04:45         
4252          303          11/06/2019 11:04:45        
4252          317          19/06/2019 11:04:45              

Индексируются столбцы даты и user_id. Без упорядочения строки не в каком-либо определенном порядке в базе данных, если это имеет значение.

Дальнейшее, что я получил с этой проблемой, является запрос, подобный этому для текущего года (700k точек данных):

    SELECT user_id, 
    MIN(date) as date, data
    FROM datapoint_table 
    WHERE date >= '2019-01-14'
    GROUP BY user_id

Это дает мне правильную дату и user_id очень быстро, примерно ~ 0.05s. Но, как и в случае с наибольшими числами для каждой группы, остальная часть строки (в данном случае данные) не совпадает с строкой с датой. Я читал о других подобных вопросах и пробовал с подзапросом, как это:

SELECT a.user_id, a.date, a.data
FROM datapoint_table a
INNER JOIN (
    SELECT datapoint_table.user_id, 
    MIN(date) as date, data
    FROM datapoint_table 
    WHERE date >= '2019-01-01'
    GROUP BY user_id
) b ON a.user_id = b.user_id AND a.date = b.date

Этот запрос занимает около 15 секунд, чтобы получить правильное значение данных. 15-е годы слишком длинны, и я должен делать что-то не так, когда первый запрос очень быстрый. Я также попытался выполнить (MAX) - (MIN) для данных с group by для user_id, но он также имел низкую производительность.

Что может быть более эффективным способом получения того же значения данных, что и дата, или даже Разница в последних и самых ранних данных для каждого пользователя?

1 Ответ

1 голос
/ 14 января 2020

Если вы используете довольно последнюю версию MariaDB или MySQL, то ROW_NUMBER, вероятно, будет наиболее эффективным способом найти самую раннюю запись для каждого пользователя:

WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) rn
    FROM datapoint_table
    WHERE date > '2019-01-14'
)

SELECT user_id, data, date
FROM cte
WHERE rn = 1;

Для Вы можете также рассмотреть возможность добавления следующего индекса:

CREATE INDEX ON datapoint_table (user_id, date);

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

CREATE INDEX ON datapoint_table (date, user_id);

Не ясно, какая версия индекса будет выполнять лучше всего, что будет зависеть от ваших данных и плана выполнения. В идеале один из двух вышеуказанных индексов поможет базе данных выполнить ROW_NUMBER вместе с предложением WHERE.

Если ваша версия базы данных не поддерживает ROW_NUMBER, то вы можете продолжить свой текущий подход:

SELECT d1.user_id, d1.data, d1.date
FROM datapoint_table d1
INNER JOIN
(
    SELECT user_id, MIN(date) AS min_date
    FROM datapoint_table
    WHERE date > '2019-01-14'
    GROUP BY user_id
) d2
    ON d1.user_id = d2.user AND d1.date = d2.min_date
WHERE
    d1.date > '2019-01-14';

Опять же, предложенные индексы должны как минимум ускорить выполнение подзапроса GROUP BY.

...