sql >> Database >  >> RDS >> PostgreSQL

Langzame volgorde van zoekopdrachten door een kolom in een samengevoegde tabel

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.



  1. De beste manier om de PK-gids van de ingevoegde rij te krijgen

  2. Aan de slag met het afstemmen van prestaties in Azure SQL Database

  3. oracle - converteer veel datumnotaties naar een enkele opgemaakte datum

  4. Een gebruiker aanmaken op MySQL