Ik breek mijn hoofd al een tijdje over dit probleem. Ik heb een PostgreSQL 8.4 database met daarin 1 tabel met ruim 4.000.000 records. Deze tabel ziet er als volgt uit:
In de tabel worden segmenten in de tijd opgeslagen, gerepresenteerd als rechthoekige boxen. Deze segmenten worden geannoteerd (de kolom value).
Nu wil ik alle segmenten vinden die geannoteerd zijn met een bepaalde value en die niet verder dan een bepaalde afstand van elkaar liggen. De volgende query levert het gewenste antwoord:
Maar zoals je al had geraden is diet niet bijzonder efficient. Het cartesisch product van sub-queries a en b loopt nogal uit de hand. Is er een manier om dit efficienter te doen? In een willekeurige programmeertaal zou ik hier een sliding window aanpak kiezen. Kan dat ook in PostgreSQL? Bijvoorbeeld zoiets als:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| CREATE TABLE metadata ( id serial NOT NULL, "value" text NOT NULL DEFAULT ''::text, segment_box box NOT NULL DEFAULT box(point(((-9223372036854775808)::bigint)::double precision, (1)::double precision), point((9223372036854775807::bigint)::double precision, ((-1))::double precision)), CONSTRAINT metadata_pk PRIMARY KEY (id) ) CREATE INDEX metadata_segment_box_ix ON metadata USING gist (segment_box); CREATE INDEX metadata_tag_value_ix ON metadata USING btree (value); |
In de tabel worden segmenten in de tijd opgeslagen, gerepresenteerd als rechthoekige boxen. Deze segmenten worden geannoteerd (de kolom value).
Nu wil ik alle segmenten vinden die geannoteerd zijn met een bepaalde value en die niet verder dan een bepaalde afstand van elkaar liggen. De volgende query levert het gewenste antwoord:
SQL:
1
2
3
| SELECT * FROM (SELECT * FROM metadata WHERE value='X') a, (SELECT * FROM metadata WHERE AND value='Y') b WHERE a.segment_box <-> b.segment_box <= 3000 |
Maar zoals je al had geraden is diet niet bijzonder efficient. Het cartesisch product van sub-queries a en b loopt nogal uit de hand. Is er een manier om dit efficienter te doen? In een willekeurige programmeertaal zou ik hier een sliding window aanpak kiezen. Kan dat ook in PostgreSQL? Bijvoorbeeld zoiets als:
SQL:
1
2
3
| SELECT *, rank() OVER ( PARTITION BY "value" ORDER BY (segment_box[1])[0], (segment_box[0])[0] ) FROM metadata WHERE value='X' OR value='Y' |