GIS-Lab: Руководство по PostGIS: 4.6. Сложные запросы

Руководство по PostGIS

4.6. Сложные запросы

<<< предыдущая глава | оглавление | следующая глава >>>

Смысл функциональности пространственных баз данных заключается в выполнении запросов к базе данных, для которых иначе потребовалась бы настольная ГИС. Эффективное использование PostGIS требует знания доступных пространственных функций и умения создавать индексы, обеспечивающие эффективную работу.

4.6.1. Преимущества индексов

Конструируя запрос важно помнить, что только операторы работающие с охватами (bounding-box-based), типа && могут использовать пространственные индексы GiST. Такие функции, как distance() не могут использовать индекс для оптимизации своих операций. Например, следующий запрос был бы очень медленным на большой таблице:

SELECT the_geom
FROM geom_table
WHERE ST_Distance(the_geom, GeomFromText('POINT(100000 200000)', -1)) < 100

Этот запрос выбирает все геометрии из geom_table, которые находятся на расстоянии в 100 единиц от точки (100000, 200000). Он будет выполняться медленно, так как влечет за собой вычисления расстояний между каждой точкой в таблице и нашей точкой, т.е. одно вычисление ST_Distance() для каждой строки таблицы. Мы можем избежать этого с помощью оператора &&, уменьшающего число необходимых вычислений:

SELECT the_geom
FROM geom_table
WHERE the_geom && 'BOX3D(90900 190900, 100100 200100)'::box3d
  AND
ST_Distance(the_geom, GeomFromText('POINT(100000 200000)', -1)) < 100

Этот запрос выбирает те-же геометрии, но делает это более эффективно. При условии, что для the_geom существует индекс GiST, планировщик будет считать, что использование этого индекса уменьшит число строк, на которых неоходимо вычислять функцию distance(). Заметим, что геометрия BOX3D, которая используется с оператором &&, является прямоугольником со стороной в 200 единиц, центрированным в нужной точке - это наш "прямоугольник запроса" ("query box"). Оператор && использует индекс, чтобы быстро уменьшить число рассматриваемых записей, выбирая только те геометрии, которые пересекаются с "прямоугольником запроса". Если предположить, что наш прямоугольник запросов намного меньше, чем все геометрии в таблице, то это позволит резко сократить число вычислений расстояний, которые должны быть сделаны.

Изменение поведения

Начиная с PostGIS 1.3.0 большинство функций геометрических отношений (Geometry Relationship Functions), за исключением ST_Disjoint и ST_Relate, скрыто включают операторы определения охвата.

4.6.2. Примеры пространственного SQL

Примеры этого раздела будут использовать две таблицы: таблицу улиц (линии) и таблицу границ муниципалитетов (полигоны). Таблица bc_roads будет определена так:

Column      | Type              | Description
------------+-------------------+-------------------
gid         | integer           | Уникальный ID
name        | character varying | Имя улицы
the_geom    | geometry          | Геометрия расположения (Linestring)

Таблица bc_municipality будет определена так:

Column     | Type              | Description
-----------+-------------------+-------------------
gid        | integer           | Уникальный ID
code       | integer           | Уникальный ID
name       | character varying | Название муниципалитета / города
the_geom   | geometry          | Геометрия расположения (Polygon)
4.6.2.1. Какова общая длина всех улиц в километрах?
4.6.2.2. Какова площадь города "Prince George" в гектарах?
4.6.2.3. Какой муниципалитет является самым большим в провинции по площади?
4.6.2.4. Какова длина улиц, полностью находящихся в пределах своего муниципалитета?
4.6.2.5. Создать новую таблицу со всеми улицами в городе "Prince George".
4.6.2.6. Какова длина в километрах улицы "Douglas St" в городе "Victoria"?
4.6.2.7. Какой муниципалитет является наибольшим из тех, чей полигон содержит дырку?
4.6.2.1.

Какова общая длина всех улиц в километрах?

Ответ на этот вопрос может дать очень простой SQL-запрос:

SELECT sum(ST_Length(the_geom))/1000 AS km_roads FROM bc_roads;
			
km_roads
------------------
70842.1243039643
(1 row)

 

4.6.2.2.

Какова площадь города "Prince George" в гектарах?

В этом запросе скомбинированы атрибутивное условие (имя города) и пространственное вычисление (площадь):

SELECT
  ST_Area(the_geom)/10000 AS hectares
FROM bc_municipality
WHERE name = 'PRINCE GEORGE';
			
hectares
------------------
32657.9103824927
(1 row)
4.6.2.3.

Какой муниципалитет является самым большим в провинции по площади?

Этот запрос производит пространственные измерения согласно условиям вопроса. Есть несколько путей решения этой проблемы, но наиболее эффективным является следующий:

SELECT
  name,
  ST_Area(the_geom)/10000 AS hectares
FROM
  bc_municipality
ORDER BY hectares DESC
LIMIT 1;
			
name           | hectares
---------------+-----------------
TUMBLER RIDGE  | 155020.02556131
(1 row)

Заметим, что для ответа на данный вопрос, мы должны вычислить площадь каждого полигона. Чтобы не делать этого постоянно, имеет смысл создать в таблице столбец площадей с отдельным индексом для повышения производительности. Отсортировав результаты по убыванию и использовав команду PostgreSQL "LIMIT" мы сможем легко выбрать наибольшее значение без помощи таких агрегирующих функций, как max().

4.6.2.4.

Какова длина улиц, полностью находящихся в пределах своего муниципалитета?

Это - пример "пространственного объединения " (spatial join) , в котором объединяются данные из двух таблиц (join), но вместо обычного реляционного подхода к объединению (по внешнему ключу), в качестве условия объединения используется пространственное условие взаимоположения ("содержится в"):

SELECT
  m.name,
  sum(ST_Length(r.the_geom))/1000 as roads_km
FROM
  bc_roads AS r,
  bc_municipality AS m
WHERE
  ST_Contains(m.the_geom,r.the_geom)
GROUP BY m.name
ORDER BY roads_km;
			
name                        | roads_km
----------------------------+------------------
SURREY                      | 1539.47553551242
VANCOUVER                   | 1450.33093486576
LANGLEY DISTRICT            | 833.793392535662
BURNABY                     | 773.769091404338
PRINCE GEORGE               | 694.37554369147
...

Этот запрос выполняется заметное время, так как все дороги в таблице влияют на конечный результат (для нашего конкретного примера таблицы - это около 250K дорог). Для небольших покрытий (от нескольких сотен, до нескольких тысяч записей) ответ может быть очень быстрым.

4.6.2.5.

Создать новую таблицу со всеми улицами в городе "Prince George".

Это - пример "наложения" (overlay) , которое использует пространственные данные из двух таблиц и заносит результат в новую таблицу. В отличие от показанного выше "пространственного объединения ", запрос создает новые геометрии. Оверлей похож на пространственный join с турбонаддувом, и полезен для более точной аналитической работы:

CREATE TABLE pg_roads as
SELECT
  ST_Intersection(r.the_geom, m.the_geom) AS intersection_geom,
  ST_Length(r.the_geom) AS rd_orig_length,
  r.*
FROM
  bc_roads AS r,
  bc_municipality AS m
WHERE ST_Intersects(r.the_geom, m.the_geom)
  AND m.name = 'PRINCE GEORGE';
4.6.2.6.

Какова длина в километрах улицы "Douglas St" в городе "Victoria"?

SELECT
  sum(ST_Length(r.the_geom))/1000 AS kilometers
FROM
  bc_roads r,
  bc_municipality m
WHERE ST_Contains(m.the_geom, r.the_geom)
  AND r.name = 'Douglas St'
  AND m.name = 'VICTORIA';
			
kilometers
------------------
4.89151904172838
(1 row)
4.6.2.7.

Какой муниципалитет является наибольшим из тех, чей полигон содержит дырку?

 
SELECT gid, name, ST_Area(the_geom) AS area
FROM bc_municipality
WHERE ST_NRings(the_geom) > 1
ORDER BY area DESC LIMIT 1;
			
gid  | name         | area
-----+--------------+------------------
12   | SPALLUMCHEEN | 257374619.430216
(1 row)

Последнее обновление: September 09 2008
(Наверх)

Back to top

(С) Виктор Вислобоков, 2008-2023