In Ihrem obigen Beispielcode wird der Index explizit als erstellt NULLS LAST
und die Abfrage wird implizit ausgeführt NULLS FIRST
(was die Standardeinstellung ist ORDER BY .. DESC
), sodass PostgreSQL die Daten neu sortieren müsste, wenn der Index verwendet würde. Infolgedessen würde der Index die Abfrage tatsächlich um ein Vielfaches langsamer machen als selbst der (bereits langsame) Tabellenscan.
rds-9.6.5 root@db1=> create table performance (id integer, installs integer, hour timestamp without time zone);
CREATE TABLE
Time: 28.100 ms
rds-9.6.5 root@db1=> with generator as (select generate_series(1,166530) i)
[more] - > insert into performance (
[more] ( > select
[more] ( > i id,
[more] ( > (random()*1000)::integer installs,
[more] ( > (now() - make_interval(secs => i))::timestamp installs
[more] ( > from generator
[more] ( > );
INSERT 0 166530
Time: 244.872 ms
rds-9.6.5 root@db1=> create index hour_idx
[more] - > on performance
[more] - > using btree
[more] - > (hour desc nulls last);
CREATE INDEX
Time: 67.089 ms
rds-9.6.5 root@db1=> vacuum analyze performance;
VACUUM
Time: 43.552 ms
Wir können WHERE
der Stundenspalte eine Klausel hinzufügen, damit die Verwendung des Index eine gute Idee wird. Beachten Sie jedoch, dass wir die Daten aus dem Index noch neu sortieren müssen.
rds-9.6.5 root@db1=> explain select hour from performance where hour>now() order by hour desc limit 10;
QUERY PLAN
---------------------------------------------------------------------------------------------
Limit (cost=4.45..4.46 rows=1 width=8)
-> Sort (cost=4.45..4.46 rows=1 width=8)
Sort Key: hour DESC
-> Index Only Scan using hour_idx on performance (cost=0.42..4.44 rows=1 width=8)
Index Cond: (hour > now())
(5 rows)
Time: 0.789 ms
Wenn wir NULLS LAST
Ihrer Abfrage ein explizites Element hinzufügen , wird der Index wie erwartet verwendet.
rds-9.6.5 root@db1=> explain select hour from performance order by hour desc NULLS LAST limit 10;
QUERY PLAN
-----------------------------------------------------------------------------------------------
Limit (cost=0.42..0.68 rows=10 width=8)
-> Index Only Scan using hour_idx on performance (cost=0.42..4334.37 rows=166530 width=8)
(2 rows)
Time: 0.526 ms
Wenn wir alternativ das (nicht standardmäßige) NULLS LAST
aus Ihrem Index löschen, wird es von der Abfrage wie erwartet ohne Änderung verwendet.
rds-9.6.5 root@db1=> drop index hour_idx;
DROP INDEX
Time: 4.124 ms
rds-9.6.5 root@db1=> create index hour_idx
[more] - > on performance
[more] - > using btree
[more] - > (hour desc);
CREATE INDEX
Time: 69.220 ms
rds-9.6.5 root@db1=> explain select hour from performance order by hour desc limit 10;
QUERY PLAN
-----------------------------------------------------------------------------------------------
Limit (cost=0.42..0.68 rows=10 width=8)
-> Index Only Scan using hour_idx on performance (cost=0.42..4334.37 rows=166530 width=8)
(2 rows)
Time: 0.725 ms
Beachten Sie, dass Sie das auch DESC
aus Ihrem Index löschen können. PostgreSQL kann Indizes sowohl vorwärts als auch rückwärts scannen, und bei einspaltigen Indizes ist es im Allgemeinen nicht erforderlich, sie umzukehren. Sie müssen nur vorsichtig sein, wenn Sie die richtige Kombination aus Reihenfolge und Nullen zuerst / zuletzt haben.
rds-9.6.5 root@db1=> drop index hour_idx;
DROP INDEX
Time: 3.837 ms
rds-9.6.5 root@db1=> create index hour_idx
[more] - > on performance
[more] - > using btree
[more] - > (hour);
CREATE INDEX
Time: 94.815 ms
rds-9.6.5 root@db1=> explain select hour from performance order by hour desc limit 10;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Limit (cost=0.42..0.68 rows=10 width=8)
-> Index Only Scan Backward using hour_idx on performance (cost=0.42..4334.37 rows=166530 width=8)
(2 rows)
Time: 0.740 ms
VACUUM ANALYZE performance;
?