Как эффективно искать последнюю запись, соответствующую условию в Rails и PostgreSQL? - PullRequest
5 голосов
/ 21 ноября 2011

Предположим, вы хотите найти последнюю запись, введенную в базу данных (самый высокий идентификатор) , соответствующую строке: Model.where(:name => 'Joe').Есть более 100 000 записей.Есть много совпадений (скажем, тысяч).

Какой самый эффективный способ сделать это?Должен ли PostgreSQL найти все записи или он может просто найти последнюю?Это очень медленный запрос?

Работа в Rails 3.0.7, Ruby 1.9.2 и PostgreSQL 8.3.

Ответы [ 3 ]

8 голосов
/ 21 ноября 2011

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

Создать схему x для тестирования:

-- DROP SCHEMA x CASCADE;  -- to wipe it all for a retest or when done.
CREATE SCHEMA x;
CREATE TABLE x.tbl(id serial, name text);

Вставить 10000 случайных строк:

INSERT INTO x.tbl(name) SELECT 'x' || generate_series(1,10000);

Вставить еще 10000 строк с помощьюповторяющиеся имена:

INSERT INTO x.tbl(name) SELECT 'y' || generate_series(1,10000)%20;

Удалите случайные 10%, чтобы сделать его более реальным:

DELETE FROM x.tbl WHERE random() < 0.1;

ANALYZE x.tbl;

Запрос может выглядеть следующим образом:

SELECT *
FROM   x.tbl
WHERE  name = 'y17'
ORDER  BY id DESC
LIMIT  1;

->Общее время выполнения: 5,535 мс

CREATE INDEX tbl_name_idx on x.tbl(name);

-> Общее время выполнения: 1,228 мс

DROP INDEX x.tbl_name_idx;
CREATE INDEX tbl_name_id_idx on x.tbl(name, id);

-> Общее время выполнения: 0,053 мс

DROP INDEX x.tbl_name_id_idx;
CREATE INDEX tbl_name_id_idx on x.tbl(name, id DESC);

-> Общее время выполнения: 0,048 мс

DROP INDEX x.tbl_name_id_idx;
CREATE INDEX tbl_name_idx on x.tbl(name);
CLUSTER x.tbl using tbl_name_idx;

-> Общее время выполнения: 1,144 мс

DROP INDEX x.tbl_name_id_idx;
CREATE INDEX tbl_name_id_idx on x.tbl(name, id DESC);
CLUSTER x.tbl using tbl_name_id_idx;

-> Общее время выполнения: 0,047 мс

Заключение

С индексом подбора запрос выполняется на более чем в 100 раз быстрее .
Самым эффективным является многостолбцовый индекс с первым столбцом фильтра и последним столбцом сортировки.
Соответствие сортировкапорядок в индексе немного помогает в этом случае.

Кластеризация помогает с простым индексом, потому что все еще многие столбцы должны быть прочитаны из таблицы, и тИх можно найти в соседних блоках после кластеризации.В этом случае это не поможет с многоколоночным индексом, потому что из таблицы нужно извлечь только одну запись.
Подробнее о многоколоночных индексах в руководстве .

Всеиз этих эффектов растут с размером таблицы.10000 строк двух крошечных столбцов - это очень маленький тестовый пример.

6 голосов
/ 21 ноября 2011

Вы можете собрать запрос в Rails, и ORM напишет правильный SQL:

Model.where(:name=>"Joe").order('created_at DESC').first

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

0 голосов
/ 21 ноября 2011

Это, наверное, самый простой:

SELECT [columns] FROM [table] WHERE [criteria] ORDER BY [id column] DESC LIMIT 1

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

...