Przypadek testowy
PostgreSQL 9.1. Przetestuj bazę danych o ograniczonych zasobach, ale wystarczająco dla tego małego przypadku. Odpowiedni będzie język sortowania:
SHOW LC_COLLATE;
de_AT.UTF-8
Krok 1) Zrekonstruuj surowe środowisko testowe
-- 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);
Krok 2) ANALIZA
ANALYZE x.product;
ANALYZE x.django_site;
Krok 3) Zmień kolejność BY random()
-- DROP TABLE x.p;
CREATE TABLE x.p AS
SELECT *
FROM x.product
ORDER BY random();
ANALYZE x.p;
Wyniki
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) ANALIZA WSTĘPNA (-> skanowanie indeksu bitmapy)
2) ANALIZA KOŃCOWA (-> skanowanie sekwencyjne)
3) Zmiana kolejności losowo(), ANALIZA
ORDER BY d.domain, p.ordering, p.name
1) Całkowity czas pracy:1253.543 ms
2) Całkowity czas pracy:1250.351 ms
3) Całkowity czas pracy:1283.111 ms
ORDER BY p.ordering, p.name
1) Całkowity czas pracy:177,266 ms
2) Całkowity czas pracy:174,556 ms
3) Całkowity czas pracy:177,797 ms
ORDER BY d.id, p.ordering, p.name
1) Całkowity czas działania:176,628 ms
2) Całkowity czas działania:176,811 ms
3) Całkowity czas działania:178,150 ms
Planista oczywiście bierze pod uwagę ten d.id jest funkcjonalnie zależny.
ORDER BY d.int_col, p.ordering, p.name -- integer column in other table
1) Całkowity czas pracy:242,218 ms -- !!
2) Całkowity czas pracy:245,234 ms
3) Całkowity czas pracy:254,581 ms
Plannerowi najwyraźniej brakuje tego d.int_col
(NOT NULL) jest tak samo zależny funkcjonalnie. Ale sortowanie według kolumny liczb całkowitych jest tanie.
ORDER BY p.name -- varchar(255) in same table
1) Całkowity czas pracy:2259.171 ms -- !!
2) Całkowity czas pracy:2257.650 ms
3) Całkowity czas pracy:2258.282 ms
Sortowanie według (długiego) varchar
lub tekst
kolumna jest droga...
ORDER BY p.name COLLATE "C"
1) Całkowity czas pracy:327.516 ms -- !!
2) Całkowity czas pracy:325.103 ms
3) Całkowity czas pracy:327.206 ms
... ale nie tak drogie, jeśli zrobisz to bez lokalizacji.
Po usunięciu ustawień regionalnych, sortowanie według varchar
kolumna nie jest całkiem, ale prawie tak samo szybka. Ustawienia regionalne "C"
to w rzeczywistości „brak ustawień regionalnych, wystarczy uporządkować według wartości bajtów”. Cytuję instrukcję:
Jeśli chcesz, aby system zachowywał się tak, jakby nie miał obsługi lokalizacji, użyj specjalnej nazwy lokalizacji C lub równoważnej POSIX.
Podsumowując, @dvd wybrał:
ORDER BY d.domain COLLATE "C", p.ordering, p.name
...3) Całkowity czas pracy:275,854 ms
To powinno wystarczyć.