Тестов случай
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) Пренаредете BY random()
-- 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) След АНАЛИЗ (-> последователно сканиране)
3) Пренареждане чрез произволен(), АНАЛИЗ
ORDER BY d.domain, p.ordering, p.name
1) Общо време на изпълнение:1253,543 ms
2) Общо време на изпълнение:1250,351 ms
3) Общо време на изпълнение:1283,111 ms
ORDER BY p.ordering, p.name
1) Общо време на изпълнение:177,266 ms
2) Общо време на изпълнение:174,556 ms
3) Общо време на изпълнение:177,797 ms
ORDER BY d.id, p.ordering, p.name
1) Общо време на изпълнение:176,628 ms
2) Общо време на изпълнение:176,811 ms
3) Общо време на изпълнение:178,150 ms
Планиращият очевидно взема предвид този d.id> код> е функционално зависима.
ORDER BY d.int_col, p.ordering, p.name -- integer column in other table
1) Общо време на изпълнение:242,218 ms -- !!
2) Общо време на изпълнение:245,234 ms
3) Общо време на изпълнение:254,581 ms
Планиращият очевидно пропуска този d.int_col
(NOT NULL) е също толкова функционално зависима. Но сортирането по колона с цяло число е евтино.
ORDER BY p.name -- varchar(255) in same table
1) Общо време на изпълнение:2259,171 ms -- !!
2) Общо време на изпълнение:2257,650 ms
3) Общо време на изпълнение:2258,282 ms
Сортиране по (дълъг) varchar
или текст
колона е скъпа ...
ORDER BY p.name COLLATE "C"
1) Общо време на изпълнение:327,516 ms -- !!
2) Общо време на изпълнение:325,103 ms
3) Общо време на изпълнение:327,206 ms
... но не толкова скъпо, ако се прави без локал.
Без локал, сортиране по varchar
колоната не е съвсем, но почти толкова бърза. Локал "C"
е ефективно "без локал, просто подреждане по стойност на байта". Цитирам ръководството:
Ако искате системата да се държи така, сякаш няма поддръжка за локал, използвайте специалното име на локал C или еквивалентно POSIX.
Обединявайки всичко това, @dvd избра:
ORDER BY d.domain COLLATE "C", p.ordering, p.name
...3) Общо време на изпълнение:275,854 ms
Това трябва да стане.