ST_D с иногда не использует индекс - PullRequest
0 голосов
/ 12 мая 2018

Я использую PostGIS с Postgresql, чтобы иметь возможность находить записи в пределах некоторого радиуса по координатам, хранящимся в столбце местоположения Geometry/Point SRID: 4326.Вот два запроса, над которыми я экспериментирую:

Первый с расстоянием в метрах и use_spheroid = true

EXPLAIN ANALYZE SELECT count(*) FROM "cars" WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(20, -30), 4326), 105000, true) LIMIT 1000;
                                                                                                                                                                                             QUERY PLAN                                                                                                                                                                                              
--------------
 Limit  (cost=11884.28..11884.30 rows=1 width=8) (actual time=18.843..18.844 rows=1 loops=1)
   ->  Aggregate  (cost=11884.28..11884.30 rows=1 width=8) (actual time=18.842..18.843 rows=1 loops=1)
         ->  Seq Scan on cars  (cost=0.00..11883.33 rows=381 width=0) (actual time=0.486..18.827 rows=38 loops=1)
               Filter: (((location)::geography && '0101000020E610000000000000000034400000000000003EC0'::geography) AND ('0101000020E610000000000000000034400000000000003EC0'::geography && _st_expand((location)::geography, '105000'::double precision)) AND _st_dwithin((location)::geography, '0101000020E610000000000000000034400000000000003EC0'::geography, '105000'::double precision, true))
               Rows Removed by Filter: 28549
 Planning time: 0.166 ms
 Execution time: 18.878 ms
(7 rows)

Второй, я предполагаю, принимает расстояние в градусах, а use_spheroid имеет значение falseдефолт.ИСПРАВЛЕНИЕ: Оказалось, что это все еще использует use_spheroid = true, но сигнатура функции, соответствующая этому вызову, ожидает геометрические единицы и единицы SRID, которые являются градусами для 4326.

EXPLAIN ANALYZE SELECT count(*) FROM "cars" WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(20, -30), 4326), 1) LIMIT 1000;
                                                                                                                          QUERY PLAN                                                                                                                          
-----------------------------
 Limit  (cost=145.30..145.31 rows=1 width=8) (actual time=0.154..0.155 rows=1 loops=1)
   ->  Aggregate  (cost=145.30..145.31 rows=1 width=8) (actual time=0.154..0.154 rows=1 loops=1)
         ->  Bitmap Heap Scan on cars  (cost=4.59..145.29 rows=3 width=0) (actual time=0.050..0.147 rows=37 loops=1)
               Recheck Cond: (location && '0103000020E6100000010000000500000000000000000033400000000000003FC000000000000033400000000000003DC000000000000035400000000000003DC000000000000035400000000000003FC000000000000033400000000000003FC0'::geometry)
               Filter: (('0101000020E610000000000000000034400000000000003EC0'::geometry && st_expand(location, '1'::double precision)) AND _st_dwithin(location, '0101000020E610000000000000000034400000000000003EC0'::geometry, '1'::double precision))
               Rows Removed by Filter: 11
               Heap Blocks: exact=47
               ->  Bitmap Index Scan on cars_location_index  (cost=0.00..4.59 rows=42 width=0) (actual time=0.037..0.037 rows=48 loops=1)
                     Index Cond: (location && '0103000020E6100000010000000500000000000000000033400000000000003FC000000000000033400000000000003DC000000000000035400000000000003DC000000000000035400000000000003FC000000000000033400000000000003FC0'::geometry)
 Planning time: 0.280 ms
 Execution time: 0.188 ms
(11 rows)

Оба запроса возвращают схожие результаты (+/- из-за точности).Однако первый работает в 100 раз медленнее.Кроме того, установка use_spheroid в false не гарантирует использование индекса, он возвращается к Seq Scan, когда расстояние до малого (<0,4) или слишком большого (> 45).Это так и должно быть, или я делаю что-то не так?

ДОПОЛНЕНИЕ: После еще нескольких экспериментов я изменил типы столбцов на Geography.Point, и теперь он всегда использует индекс.Кажется, проблема решена, но я все еще путаюсь с тем поведением, которое я наблюдал с типом Geometry.

1 Ответ

0 голосов
/ 13 мая 2018

Документация ST_DWithin гласит, что первая сигнатура функции принимает типы географии, а не типы геометрии:

boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters, boolean use_spheroid);

, поскольку (location, ST_SetSRID (ST_MakePoint (20, -30), 4326)) - все геометрии, было бы разумно, чтобы выполнение функции было грязным. И я думаю, что ваша вторая функция работала правильно, потому что вы выполняли эту подпись:

boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);

И, как вы заявили, переключение типов столбцов на Geography вместо Geometry решит проблему, поскольку это даст вам правильное выполнение:

boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters);

boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters, boolean use_spheroid);

Надеюсь, это немного поможет.

Редактировать:

Нашел эту часть в документации , в которой говорится, что при вводе данных

Данные стандартного типа геометрии будут автоматически переданы в географию, если они имеют SRID 4326

это может объяснить, почему Postgres принял ваш первый вызов ST_DWithin (), поскольку postgis, очевидно, приведёт его к географии, и это также объясняет, почему выполнение занимает больше времени и игнорирует индекс, так как каждое приведение приведет к созданию нового объекта, который не проиндексировано в исходной колонке.

...