Testfall
PostgreSQL 9.1. Testdatenbank mit begrenzten Ressourcen, aber ausreichend für diesen kleinen Fall. Das Gebietsschema für die Sortierung ist relevant:
SHOW LC_COLLATE;
de_AT.UTF-8
Schritt 1) Rekonstruieren Sie die Rohtestumgebung
-- 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);
Schritt 2) ANALYSE
ANALYZE x.product;
ANALYZE x.django_site;
Schritt 3) Nach dem Zufallsprinzip neu anordnen ()
-- DROP TABLE x.p;
CREATE TABLE x.p AS
SELECT *
FROM x.product
ORDER BY random();
ANALYZE x.p;
Ergebnisse
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) Pre ANALYZE (-> Bitmap Index Scan)
2) Post ANALYZE (-> Seq Scan)
3) Reorder by random(), ANALYZE
ORDER BY d.domain, p.ordering, p.name
1) Gesamtlaufzeit:1253,543 ms
2) Gesamtlaufzeit:1250,351 ms
3) Gesamtlaufzeit:1283,111 ms
ORDER BY p.ordering, p.name
1) Gesamtlaufzeit:177,266 ms
2) Gesamtlaufzeit:174,556 ms
3) Gesamtlaufzeit:177,797 ms
ORDER BY d.id, p.ordering, p.name
1) Gesamtlaufzeit:176.628 ms
2) Gesamtlaufzeit:176.811 ms
3) Gesamtlaufzeit:178.150 ms
Der Planer berücksichtigt natürlich diese d.id
ist funktional abhängig.
ORDER BY d.int_col, p.ordering, p.name -- integer column in other table
1) Gesamtlaufzeit:242,218 ms -- !!
2) Gesamtlaufzeit:245,234 ms
3) Gesamtlaufzeit:254,581 ms
Der Planer vermisst offensichtlich diesen d.int_col
(NOT NULL) ist genauso funktional abhängig. Aber das Sortieren nach einer Integer-Spalte ist billig.
ORDER BY p.name -- varchar(255) in same table
1) Gesamtlaufzeit:2259,171 ms -- !!
2) Gesamtlaufzeit:2257,650 ms
3) Gesamtlaufzeit:2258,282 ms
Sortieren nach einem (langen) varchar
oder text
Spalte ist teuer ...
ORDER BY p.name COLLATE "C"
1) Gesamtlaufzeit:327,516 ms -- !!
2) Gesamtlaufzeit:325,103 ms
3) Gesamtlaufzeit:327,206 ms
... aber nicht so teuer, wenn es ohne Locale gemacht wird.
Wenn das Gebietsschema aus dem Weg geräumt ist, wird nach einem varchar
sortiert Spalte ist nicht ganz aber fast so schnell. Gebietsschema "C"
ist effektiv "kein Gebietsschema, nur nach Bytewert sortieren". Ich zitiere das Handbuch:
Wenn Sie möchten, dass sich das System so verhält, als hätte es keine Locale-Unterstützung, verwenden Sie den speziellen Locale-Namen C oder äquivalent POSIX.
Alles zusammengenommen hat @dvd gewählt:
ORDER BY d.domain COLLATE "C", p.ordering, p.name
...3) Gesamtlaufzeit:275,854 ms
Das sollte reichen.