Caso de prueba
PostgreSQL 9.1. Base de datos de prueba con recursos limitados, pero suficiente para este pequeño caso. La configuración regional para la intercalación será relevante:
SHOW LC_COLLATE;
de_AT.UTF-8
Paso 1) Reconstruir el entorno de prueba sin procesar
-- 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);
Paso 2) ANALIZAR
ANALYZE x.product;
ANALYZE x.django_site;
Paso 3) Reordenar POR aleatorio()
-- DROP TABLE x.p;
CREATE TABLE x.p AS
SELECT *
FROM x.product
ORDER BY random();
ANALYZE x.p;
Resultados
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) ANÁLISIS previo (-> escaneo de índice de mapa de bits)
2) ANÁLISIS posterior (-> escaneo de secuencia)
3) Reordenar al azar(), ANALIZAR
ORDER BY d.domain, p.ordering, p.name
1) Tiempo de ejecución total:1253,543 ms
2) Tiempo de ejecución total:1250,351 ms
3) Tiempo de ejecución total:1283,111 ms
ORDER BY p.ordering, p.name
1) Tiempo de ejecución total:177,266 ms
2) Tiempo de ejecución total:174,556 ms
3) Tiempo de ejecución total:177,797 ms
ORDER BY d.id, p.ordering, p.name
1) Tiempo de ejecución total:176,628 ms
2) Tiempo de ejecución total:176,811 ms
3) Tiempo de ejecución total:178,150 ms
El planificador obviamente tiene en cuenta ese d.id
es funcionalmente dependiente.
ORDER BY d.int_col, p.ordering, p.name -- integer column in other table
1) Tiempo de ejecución total:242,218 ms -- !!
2) Tiempo de ejecución total:245,234 ms
3) Tiempo de ejecución total:254,581 ms
Obviamente, al planificador se le escapa eso d.int_col
(NOT NULL) es igualmente funcionalmente dependiente. Pero ordenar por una columna de enteros es barato.
ORDER BY p.name -- varchar(255) in same table
1) Tiempo de ejecución total:2259,171 ms -- !!
2) Tiempo de ejecución total:2257,650 ms
3) Tiempo de ejecución total:2258,282 ms
Ordenar por un varchar
(largo) o text
la columna es cara...
ORDER BY p.name COLLATE "C"
1) Tiempo de ejecución total:327,516 ms -- !!
2) Tiempo de ejecución total:325,103 ms
3) Tiempo de ejecución total:327,206 ms
... pero no tan caro si se hace sin configuración regional.
Con la configuración regional fuera del camino, ordenar por un varchar
la columna no es del todo pero casi igual de rápida. Configuración regional "C"
es efectivamente "sin configuración regional, solo ordene por valor de byte". Cito el manual:
Si desea que el sistema se comporte como si no fuera compatible con la configuración regional, utilice el nombre de configuración regional especial C o POSIX equivalente.
Juntándolo todo, @dvd eligió:
ORDER BY d.domain COLLATE "C", p.ordering, p.name
...3) Duración total:275,854 ms
Eso debería funcionar.