sql >> Base de Datos >  >> RDS >> PostgreSQL

Ordenación lenta de consultas por una columna en una tabla unida

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.