оптимизация запросов sqlite - PullRequest
1 голос
/ 08 июня 2009

У меня есть таблица sqlite actions, которая выглядит примерно так:

uuid varchar (36)
actiondate int
username varchar (16)
mood int
bonus int
status varchar (80)
... bunch of other similar fields (all short varchar or int fields)

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

Я надеялся, что смогу сделать что-то вроде этого:

SELECT status, actiondate
FROM actions WHERE actiondate < 20061231
GROUP BY username
ORDER BY actiondate DESC

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

Итак, у меня есть это:

SELECT actiondate, status FROM actions
WHERE actiondate < 20061231 and
uuid = (SELECT uuid from actions as alt
        WHERE alt.username = actions.username
        ORDER BY actiondate DESC LIMIT 1)

Есть ли лучший способ сделать такой запрос? Лучшее расположение столов? В настоящее время этот тип запроса занимает ~ 400 мс на моем блоке разработки, и было бы неплохо, если бы я мог срезать 100 мс или около того (мое целевое время на самом деле составляет 100 мс, но я скептически отношусь к тому, насколько это возможно). 1015 *

У меня, очевидно, есть индексы по имени пользователя и дате (на самом деле у меня есть несколько: один, который, кажется, вполне соответствует медленному запросу; один для имени пользователя; один для даты ASC; один для даты DESC и один для UUID).

FWIW, таблица action может содержать от 100 до 30 000 строк.

Ответы [ 2 ]

2 голосов
/ 08 июня 2009

Корректность перед скоростью - ваш запрос:

SELECT actiondate, status FROM actions
WHERE actiondate < 20061231 and
uuid = (SELECT uuid from actions as alt
        WHERE alt.username = actions.username
        ORDER BY actiondate DESC LIMIT 1)

не выполняет задачу, которую вы описываете - внутренний выбор может вернуть uuid для действия, которое позже, чем 2061231, тогда внешний выбор не даст результата для этого имени пользователя. Я думаю, вы можете это исправить, переместив проверку WHERE на actiondate как AND во вложенном выделении. (Я сомневаюсь, что это ускорит ситуацию, но, по крайней мере, это должно привести к правильному поведению - дайте нам знать о том, как это повлияет на скорость!).

1 голос
/ 08 июня 2009

Ваш индекс должен охватывать все столбцы, используемые в запросе для максимальной производительности.

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

Для чего-то подобного, я мог бы избежать UUID, если это возможно, а если нет, я бы позаботился о его увеличении, чтобы вы могли написать:

SELECT actiondate
    ,status
FROM actions
INNER JOIN (
    SELECT username
        ,MAX(uuid) as last_uuid from actions
    WHERE actiondate < 20061231
    GROUP BY username
) AS last_occur
    ON last_occur.username = actions.username
    AND last_occur.last_uuid = actions.uuid
WHERE actiondate < 20061231

Я думаю, что это должно хорошо работать с индексом имени пользователя ASC, uuid DESC, INCLUDE (actiondate) и индексом actiondate DESC, именем пользователя ASC, INCLUDE (status), но, очевидно, посмотрите на план запроса.

Без увеличения uuids вам понадобится какое-то правило, гарантирующее, что вы выбираете последнее действие для человека, поскольку, если имя пользователя, actiondate не является уникальным, в вашем исходном ORDER BY actiondate DESC ограничение 1 не гарантирует вас выбирают правильный ряд каждый раз. Если имя пользователя, actiondate уникальны, вы можете использовать следующее:

SELECT actiondate
    ,status
FROM actions
INNER JOIN (
    SELECT username
        ,MAX(actiondate) as last_actiondate from actions
    WHERE actiondate < 20061231
    GROUP BY username
) AS last_occur
    ON last_occur.username = actions.username
    AND last_occur.last_actiondate = actions.actiondate
WHERE actiondate < 20061231

Если он не уникален, он все равно будет работать, но вы получите несколько действий для человека в его последнюю дату действия. В этом случае рекомендуемые индексы также будут другими (и лучше), поскольку большой uuid не требуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...