Testcase
PostgreSQL 9.1. Testdatabase met beperkte middelen, maar genoeg voor dit kleine geval. De landinstelling voor sortering is relevant:
SHOW LC_COLLATE;
de_AT.UTF-8
Stap 1) Reconstrueer de onbewerkte testomgeving
-- 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);
Stap 2) ANALYSEER
ANALYZE x.product;
ANALYZE x.django_site;
Stap 3) Opnieuw ordenen DOOR willekeurig()
-- DROP TABLE x.p;
CREATE TABLE x.p AS
SELECT *
FROM x.product
ORDER BY random();
ANALYZE x.p;
Resultaten
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) ANALYSEREN vooraf (-> bitmapindexscan)
2) ANALYSEREN achteraf (-> seq-scan)
3) Opnieuw ordenen door willekeurig(), ANALYSEREN
ORDER BY d.domain, p.ordering, p.name
1) Totale looptijd:1253.543 ms
2) Totale looptijd:1250.351 ms
3) Totale looptijd:1283.111 ms
ORDER BY p.ordering, p.name
1) Totale looptijd:177.266 ms
2) Totale looptijd:174.556 ms
3) Totale looptijd:177.797 ms
ORDER BY d.id, p.ordering, p.name
1) Totale looptijd:176.628 ms
2) Totale looptijd:176.811 ms
3) Totale looptijd:178.150 ms
De planner houdt uiteraard rekening met die d.id
is functioneel afhankelijk.
ORDER BY d.int_col, p.ordering, p.name -- integer column in other table
1) Totale looptijd:242,218 ms -- !!
2) Totale looptijd:245,234 ms
3) Totale looptijd:254,581 ms
De planner mist duidelijk die d.int_col
(NIET NULL) is net zo functioneel afhankelijk. Maar sorteren op een geheeltallige kolom is goedkoop.
ORDER BY p.name -- varchar(255) in same table
1) Totale looptijd:2259.171 ms -- !!
2) Totale looptijd:2257,650 ms
3) Totale looptijd:2258.282 ms
Sorteren op een (lange) varchar
of text
kolom is duur ...
ORDER BY p.name COLLATE "C"
1) Totale looptijd:327,516 ms -- !!
2) Totale looptijd:325,103 ms
3) Totale looptijd:327.206 ms
... maar niet zo duur als je het zonder locale doet.
Met de landinstelling uit de weg, sorteren op een varchar
kolom is niet helemaal maar bijna net zo snel. Landinstelling "C"
is in feite "geen landinstelling, alleen volgorde op bytewaarde". Ik citeer de handleiding:
Als u wilt dat het systeem zich gedraagt alsof het geen landinstelling ondersteunt, gebruikt u de speciale landinstelling C, of equivalent POSIX.
Alles bij elkaar, @dvd koos:
ORDER BY d.domain COLLATE "C", p.ordering, p.name
...3) Totale looptijd:275.854 ms
Dat zou voldoende moeten zijn.