Есть ли способ установить несколько значений столбцов в одном пространственном запросе? - PullRequest
2 голосов
/ 01 мая 2020

Я связываю атрибуты с точками, используя PostGIS ST_DWIthin().

UPDATE scratch.intersections AS i
SET legs = (
    SELECT COUNT(r.geom)
    FROM received.streets r
    WHERE ST_DWithin(i.geom, r.geom, 2));

UPDATE scratch.intersections AS i
SET streets = (
    SELECT ARRAY_AGG(DISTINCT r.NAME ORDER BY r.NAME) 
    filter (WHERE r.NAME IS NOT NULL)
    FROM received.streets r
    WHERE ST_DWithin(i.geom, r.geom, 2));

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

Есть ли способ превратить эти два запроса в один запрос, требующий только одного пространственного вычисления?

Было бы эффективнее создать INNER JOIN создание новой временной таблицы с запись для каждой строки в пределах 2 от точки, а затем установить значения из этой таблицы? Описывая его, он звучит менее эффективно, поскольку производительность ST_DWithin() не страшна при использовании индексов.

Ответы [ 2 ]

2 голосов
/ 03 мая 2020

JGH предоставил умный способ UPDATE нескольких столбцов одновременно из (коррелированного) подзапроса. Возможно с Postgres 9.5. Однако есть существенные недостатки.

Эта форма обновляет каждую строку в таблице безоговорочно . Вам придется повторить вычисление в предложении WHERE, чтобы сделать его условно, что не соответствует цели сделать это только один раз.

В Postgr SQL модель MV CC модель UPDATE По сути, это означает написание новой версии полной строки, таблицы с разбуханием и индексов. Само по себе дорого, но также требует много дополнительной работы для VACUUM - и / или снижения производительности, если вы не можете или не убираете. См .:

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

Однако некоторые (или многие / большинство?) Строки могут быть уже устаревшими. Тогда это огромные отходы . Вместо этого рассмотрим:

UPDATE scratch.intersections AS i
SET    legs    = r.legs  
     , streets = r.streets
FROM   scratch.intersections x
JOIN   LATERAL (
   SELECT count(*) AS legs  -- assuming r.geom is NOT NULL
        , array_agg(DISTINCT s.NAME ORDER BY s.NAME)
                    FILTER (WHERE s.NAME IS NOT NULL) AS streets
   FROM   received.streets s
   WHERE  ST_DWithin(x.geom, s.geom, 2)
   ) r ON x.legs    IS DISTINCT FROM r.legs
       OR x.streets IS DISTINCT FROM r.streets 
WHERE i.id = x.id;  -- id being the PK (or at least UNIQUE)

Это не касается строк, которые на самом деле не меняются.

Нам нужен дополнительный экземпляр scratch.intersections в предложении FROM, чтобы разрешить LATERAL присоединиться. Но мы исключаем все строки без изменений перед применением обновления - таким образом, сохраняя большую часть работы для каждой строки, которая уже обновлена. Ну, так как подзапрос относительно дорогой, возможно, не большинство работы. Но обычно фактическая операция записи намного дороже, чем вычисление новых значений.

Это довольно близко к тому, что вы имели в виду в своем последнем абзаце. Но более эффективно, не создавая временную таблицу, и вы также сохраняете пустые записи.

И если вам нужно только обновить потенциально затронутые строки в intersections после изменения нескольких строк в streets.geom, есть больший потенциал оптимизации , еще. Не вдаваясь в подробности, кажется, выходит за рамки этого вопроса.

Связанный:

2 голосов
/ 01 мая 2020

Вы можете обновить несколько столбцов в одной инструкции, заключив их в скобки

UPDATE myTable
SET (a,b) = (
  select c,d 
  from anotherTable 
  WHERE st_dwithin(mytable.geom, anotherTable.geom,2)
);
...