ST_DWITHIN не использует индекс GIST или BRIN - PullRequest
0 голосов
/ 12 сентября 2018

Я использую постгис-функцию ST_DWithin (география gg1, география gg2, двойная точность distance_meters), чтобы определить, находится ли точка на заданном расстоянии от многоугольника.Я запускаю тесты, чтобы увидеть, сколько времени занимает запрос, и объяснение показывает, что он выполняет последовательное сканирование таблиц вместо использования индексов BRIN или GIST.Может кто-нибудь предложить способ его оптимизации.

Вот таблицы -

table1 (ident_geog) с полигонами

CREATE TABLE public.incident_geog
(
    incident_id integer NOT NULL DEFAULT nextval('incident_geog_incident_id_seq'::regclass),
    incident_name character varying(20) COLLATE pg_catalog."default",
    incident_span geography(Polygon,4326),
    CONSTRAINT incident_geog_pkey PRIMARY KEY (incident_id)
)

CREATE INDEX incident_geog_gix
    ON public.incident_geog USING gist
    (incident_span)

table2 с точками и расстояниями (watchzones_geog)

CREATE TABLE public.watchzones_geog
(
    id integer NOT NULL DEFAULT nextval('watchzones_geog_id_seq'::regclass),
    date_created timestamp with time zone DEFAULT now(),
    latitude numeric(10,7) DEFAULT NULL::numeric,
    longitude numeric(10,7) DEFAULT NULL::numeric,
    radius integer,
    "position" geography(Point,4326),
    CONSTRAINT watchzones_geog_pkey PRIMARY KEY (id)
)

CREATE INDEX watchzones_geog_gix
    ON public.watchzones_geog USING gist
    ("position")

Sql с st_dwithin

explain select i.incident_id,wz.id from watchzones_geog wz, incident_geog i where ST_DWithin(position,incident_span,wz.radius * 1000);

Вывод объяснения:

Nested Loop  (cost=0.26..418436.69 rows=1 width=8)
-> Seq Scan on watchzones_geog wz  (cost=0.00..13408.01 rows=600001 width=40)
 ->  Index Scan using incident_geog_gix on incident_geog i  (cost=0.26..0.67 rows=1 width=292)
        Index Cond: (incident_span && _st_expand(wz."position", ((wz.radius * 1000))::double precision))
        Filter: ((wz."position" && _st_expand(incident_span, ((wz.radius * 1000))::double precision)) AND _st_dwithin(wz."position", incident_span, ((wz.radius * 1000))::double precision, true))

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Несколько общих моментов:

  1. Используйте ИДЕНТИЧНЫЕ КОЛОННЫ вместо ручного задания последовательностей.
  2. Вам не нужно DEFAULT null:: по умолчанию всегда null для столбцов, допускающих значение NULL.
  3. Убедитесь, что вы VACUUM ANALAYZE обе таблицы после загрузки их.
  4. Не используйте SQL-89, вместо этого напишите свой INNER JOIN ... ON

    SELECT i.incident_id,wz.id
    FROM watchzones_geog wz
    INNER JOIN incident_geog i
      ON ST_DWithin(wz.position,i.incident_span,50);
    
  5. В вашем explain analyze у вас есть wz.radius * 1000 в вашем запросе, вы даете радиус как 50. Что это? Сканирует ли запрос seq, если вы статически набираете радиус?

  6. Если вы не используете широту и долготу в таблице, отбросьте эти два столбца. Нет смысла хранить их дважды.
  7. Я бы не стал использовать varchar(20), вместо этого просто использовал бы text, он быстрее, потому что нет проверки длины, и он реализован таким же образом.
0 голосов
/ 12 сентября 2018

Ваш SQL фактически выполняет поиск нескольких полигонов в пределах указанного расстояния для каждой точки.Результат однозначного соответствия между incident_geog.incident_id и watchzones_geog.id.Потому что вы работаете в каждой точке, поэтому он использует последовательное сканирование.

Полагаю, вы хотите начать с Polygon, чтобы найти точку.Таким образом, ваш SQL должен изменить таблицу.

explain select i.incident_id,wz.id from incident_geog i, watchzones_geog wz where ST_DWithin(position,incident_span,50);

мы можем видеть:

Nested Loop  (cost=0.27..876.00 rows=1 width=16)
   ->  Seq Scan on incident_geog i  (cost=0.00..22.00 rows=1200 width=40)
   ->  Index Scan using watchzones_geog_gix on watchzones_geog wz  (cost=0.27..0.70 rows=1 width=40)
         Index Cond: ("position" && _st_expand(i.incident_span, '50'::double precision))
         Filter: ((i.incident_span && _st_expand("position", '50'::double precision)) AND _st_dwithin("position", i.incident_span, '50'::double precision, true))

Поскольку вы работаете с каждым ордером, всегда есть таблица, которая пересекает все записи при последовательном сканировании,Результаты этих двух SQL не отличаются.Ключ в том, в какой таблице вы начинаете искать порядок другой таблицы.

Может быть, вы можете попробовать Parallel Query.Не используйте Parallel Query:

SET parallel_tuple_cost TO 0;
explain analyze select i.incident_id,wz.id from incident_geog i, watchzones_geog wz where ST_DWithin(position,incident_span,50);

Nested Loop  (cost=0.27..876.00 rows=1 width=16) (actual time=0.002..0.002 rows=0 loops=1)
   ->  Seq Scan on incident_geog i  (cost=0.00..22.00 rows=1200 width=40) (actual time=0.002..0.002 rows=0 loops=1)
   ->  Index Scan using watchzones_geog_gix on watchzones_geog wz  (cost=0.27..0.70 rows=1 width=40) (never executed)
         Index Cond: ("position" && _st_expand(i.incident_span, '50'::double precision))
         Filter: ((i.incident_span && _st_expand("position", '50'::double precision)) AND _st_dwithin("position", i.incident_span, '50'::double precision, true))
 Planning time: 0.125 ms
 Execution time: 0.028 ms

Попробуйте Parallel Query и установите parallel_tuple_cost как 2:

SET parallel_tuple_cost TO 2;
explain analyze select i.incident_id,wz.id from incident_geog i, watchzones_geog wz where ST_DWithin(position,incident_span,50);

Nested Loop  (cost=0.27..876.00 rows=1 width=16) (actual time=0.002..0.002 rows=0 loops=1)
       ->  Seq Scan on incident_geog i  (cost=0.00..22.00 rows=1200 width=40) (actual time=0.001..0.001 rows=0 loops=1)
       ->  Index Scan using watchzones_geog_gix on watchzones_geog wz  (cost=0.27..0.70 rows=1 width=40) (never executed)
             Index Cond: ("position" && _st_expand(i.incident_span, '50'::double precision))
             Filter: ((i.incident_span && _st_expand("position", '50'::double precision)) AND _st_dwithin("position", i.incident_span, '50'::double precision, true))
     Planning time: 0.103 ms
     Execution time: 0.013 ms
...