Тестовый кейс
PostgreSQL 9.1. Протестируйте базу данных с ограниченными ресурсами, но достаточно для этого небольшого случая. Язык сопоставления будет иметь отношение:
SHOW LC_COLLATE;
de_AT.UTF-8
Шаг 1) Восстановление исходной тестовой среды
-- DROP TABLE x;
CREATE SCHEMA x; -- test schema
-- DROP TABLE x.django_site;
CREATE TABLE x.django_site (
id serial primary key
,domain character varying(100) not null
,int_col int not null
);
INSERT INTO x.django_site values (1,'www.testsite.com/foodir/', 3);
-- DROP TABLE x.product;
CREATE TABLE x.product (
id serial primary key
,site_id integer not null
,name character varying(255) not null
,slug character varying(255) not null
,sku character varying(255)
,ordering integer not null
,active boolean not null
);
INSERT INTO x.product (site_id, name, slug, sku, ordering, active)
SELECT 1
,repeat(chr((random() * 255)::int + 32), (random()*255)::int)
,repeat(chr((random() * 255)::int + 32), (random()*255)::int)
,repeat(chr((random() * 255)::int + 32), (random()*255)::int)
,i -- ordering in sequence
,NOT (random()* 0.5174346569119122)::int::bool
FROM generate_series(1, 17540) AS x(i);
-- SELECT ((591::float8 / 17540)* 0.5) / (1 - (591::float8 / 17540))
-- = 0.5174346569119122
CREATE INDEX product_site_id on x.product(site_id);
Шаг 2) АНАЛИЗ
ANALYZE x.product;
ANALYZE x.django_site;
Шаг 3) Изменить порядок в случайном порядке ()
-- DROP TABLE x.p;
CREATE TABLE x.p AS
SELECT *
FROM x.product
ORDER BY random();
ANALYZE x.p;
Результаты
EXPLAIN ANALYZE
SELECT p.*
FROM x.p
JOIN x.django_site d ON (p.site_id = d.id)
WHERE p.active
AND p.site_id = 1
-- ORDER BY d.domain, p.ordering, p.name
-- ORDER BY p.ordering, p.name
-- ORDER BY d.id, p.ordering, p.name
-- ORDER BY d.int_col, p.ordering, p.name
-- ORDER BY p.name COLLATE "C"
-- ORDER BY d.domain COLLATE "C", p.ordering, p.name -- dvd's final solution
1) Предварительный анализ (-> сканирование растрового индекса)
2) Post ANALYZE (-> seq scan)
3) Переупорядочить случайным образом (), АНАЛИЗ
ORDER BY d.domain, p.ordering, p.name
1) Общее время выполнения: 1253,543 мс
2) Общее время работы: 1250,351 мс
3) Общее время работы: 1283,111 мс
ORDER BY p.ordering, p.name
1) Общее время выполнения: 177,266 мс
2) Общее время работы: 174,556 мс
3) Общее время работы: 177,797 мс
ORDER BY d.id, p.ordering, p.name
1) Общее время выполнения: 176,628 мс
2) Общее время работы: 176,811 мс
3) Общее время работы: 178,150 мс
Планировщик, очевидно, учитывает, что d.id
является функционально зависимым.
ORDER BY <b>d.int_col</b>, p.ordering, p.name -- integer column in other table
1) Общее время работы: 242,218 мс - !!
2) Общее время работы: 245,234 мс
3) Общее время работы: 254,581 мс
Планировщик явно упускает из виду, что d.int_col
(NOT NULL) так же функционально зависит. Но сортировка по целому столбцу стоит дешево.
ORDER BY <b>p.name</b> -- varchar(255) in same table
1) Общее время выполнения: 2259,171 мс - !!
2) Общее время работы: 2257,650 мс
3) Общее время работы: 2258,282 мс
Сортировка по (длинному) varchar
или text
столбцу стоит дорого ...
ORDER BY p.name <b>COLLATE "C"</b>
1) Общее время выполнения: 327,516 мс - !!
2) Общее время работы: 325,103 мс
3) Общее время выполнения: 327,206 мс
... но не так дорого, если обойтись без языка.
С удалением локали сортировка по столбцу varchar
не совсем, но почти такая же быстрая. Локаль "C"
по сути является "без локали, просто по байтовому значению". Я цитирую руководство :
Если вы хотите, чтобы система работала так, как если бы она не поддерживала локаль, используйте
специальное имя локали C или эквивалентно POSIX.
Собрав все вместе, @dvd выбрал:
ORDER BY d.domain COLLATE "C", p.ordering, p.name
...
3) Общее время работы: 275,854 мс
Это должно сделать.