web-dev-qa-db-ja.com

後続のループのためにRETURNテーブル(列タイプ、名前)を動的に定義する

テーブルからの行数に対してST_Intersection(clipper_geom, clipped_geom)を実行したいと思います。

https://postgis.net/docs/ST_Intersection.html

https://postgis.net/docs/ST_Intersects.html

POSTGIS交差は、ST_Intersects()とは異なり、ネイティブで複数のジオメトリの処理をサポートせず、ST_Intersects()を使用して_clipper_geom_と交差する行を選択する関数(テーブルを返す)を設計する必要がありました、結果セットをループし、ST_Intersection()を使用して各交差を計算します。 geomフィールドと_clipped_geom_wkt_は、クリップされたジオメトリを記録するフィールドです。

関数は機能しますが、クリッピングを作成するテーブルごとに異なるものが必要です。入力テーブル(列名とタイプ)を動的に読み取り、RETURNステートメントで両方を定義したいと思います。

すべてのフィールド名とタイプは同じで、geomのみが更新され、_clipped_geom_wkt_が追加されます。

私はスタックオーバーフローを検索しようとしましたが、動的テーブル構造を作成する方法の例を見つけましたが、新しいデータを挿入/更新するために列名を一致させる必要がある最初の結果に対して後続のLOOPを実行するものはありません。

これは私がこれまでに思いついたことですが、LOOP部分を実行して_clipped_geom_wkt_フィールドを追加し、geomフィールドを更新する方法がわかりません。一部の応答では、SETOFにフィールドが追加された場合、RETURNS TABLE (...)を使用するように勧めています...

https://stackoverflow.com/questions/17821482/easy-way-to-have-return-type-be-setof-table-plus-additional-fields

しかし、動的に生成された列は_RETURNS SETOF ..._でのみサポートされているようです

https://stackoverflow.com/questions/11740256/refactor-a-pl-pgsql-function-to-return-the-output-of-various-select-queries/11751557#11751557

https://stackoverflow.com/questions/8605174/postgresql-error-42601-a-column-definition-list-is-required-for-functions-ret/8611675#8611675

_CREATE OR REPLACE FUNCTION clip_palin_polygon_complete(clipped_table text,clipper_geom text, age_sequence VARCHAR)
RETURNS TABLE (rec clipped_table, clipped_geom_wkt text)) AS $$ --not sure if this is the right way to do it...
DECLARE var_r record;
BEGIN
    FOR var_r IN (
        SELECT * FROM clipped_table 
        WHERE clipped_table.seq IN (age_sequence)
        AND ST_Intersects(ST_GeomFromText(clipper_geom,4326), clipped_table.geom)
    )
    LOOP
        /* 
        these are the original table fields that I would like to keep and match
        dynamically with any table I have as input (clipped_table)
        objectid := var_r.objectid;
        seq := var_r.seq;
        -- etc.
        */

        --below there are the only two fields that need modifying
        geom := (
            SELECT ST_Intersection(ST_GeomFromText(clipper_geom, 4326), var_r.geom) AS geom);
        clipped_geom_wkt := (
            SELECT
            ST_AsText(ST_Intersection(ST_GeomFromText(clipper_geom,4326), var_r.geom)) AS clipped_geom_wkt);
        RETURN NEXT;
    END LOOP;
END; $$
LANGUAGE 'plpgsql'
_
2
user3523583

必要なもの

代わりに単純なSELECTを使用して、すべての複雑化を回避できるはずです。

_SELECT t.*, ST_Intersection(x.geom, t.geom) AS geom2
FROM   tbl t  -- replace tbl with actual table
JOIN   ST_GeomFromText($clipper_geom, 4326) AS x(geom) ON ST_Intersects(x.geom, t.geom)
WHERE  t.seq = $age_sequence;
_

小さな違い:結果の元のgeom交差__geom2_を取得します。本当に必要な場合は、冗長なST_AsText(ST_Intersection(x.geom, t.geom)) AS clipped_geom_wktSELECTリストに追加します。

あなたが尋ねたこと

次のような質問を理解しました。

さまざまな入力テーブルがあり、それぞれに_geom geometry_列があります。関数がテーブル名と_clipper_geom geometry_を入力として受け取り、_clipper_geom_と交差するすべての行を返すようにしたい。 1列_clipped_geom_wkt text_が追加され、geomの値が変更されます。各列は_clipper_geom_。との共通部分を示します

それは簡単なことではありません。 SQLは静的言語であり、関数は呼び出し時の戻り値の型を知る必要があります。ポリモーフィック型を使用すると動的な結果を得ることができますが、操作するには既知の行型が必要です。既存のテーブルの行タイプは適していますが、別の列を追加したいとします。それは簡単には不可能です。回避策は、多形の行タイプplusを追加の列で返すことです(すでに試したようです)。関数呼び出しの行タイプを分解して、目的の結果を取得します。

_CREATE OR REPLACE FUNCTION my_func(_tbl ANYELEMENT
                                 , clipper_geom text
                                 , age_sequence text)
  RETURNS TABLE (tbl ANYELEMENT, clipped_geom_wkt text) AS
$func$
DECLARE
   _geom geometry := ST_GeomFromText(clipper_geom, 4326);  -- compute once
BEGIN
   FOR tbl IN EXECUTE format(
     'SELECT * FROM %s
      WHERE  seq = $1
      AND    ST_Intersects($2, geom)'
    , pg_typeof(_tbl)
      )
   USING age_sequence  -- data type must match!
       , _geom
   LOOP          
      tbl := tbl #= hstore('geom', ST_Intersection(_geom, tbl.geom));
      clipped_geom_wkt := ST_AsText(tbl.geom);
      RETURN NEXT;
   END LOOP;
END
$func$  LANGUAGE plpgsql;  -- don't quote the language name
_

電話(重要!):

_SELECT (tbl).*, clipped_geom_wkt
FROM my_func(NULL::public.clipped_table, $my_clipper_geom, $my_age_sequence);
_

Postgres 10で動作します。

注目すべき機能:

よりシンプル

上記の設計にはこだわっていますが、この例では不要な複雑化です。追加の列_clipped_geom_wkt_は完全に冗長です。関数呼び出しのgeomからそれを派生させるだけです。次に、入力タイプを直接使用できます。

_CREATE OR REPLACE FUNCTION my_func2(_tbl ANYELEMENT
                                 , clipper_geom text
                                 , age_sequence text)
  RETURNS SETOF ANYELEMENT AS
$func$
DECLARE
   _geom geometry := ST_GeomFromText(clipper_geom, 4326);
BEGIN
   FOR _tbl IN EXECUTE format(
     'SELECT * FROM %s
      WHERE  seq = $1
      AND    ST_Intersects($2, geom)'
    , pg_typeof(_tbl)
      )
   USING age_sequence -- data type must match!
       , _geom
   LOOP
      _tbl := _tbl #= hstore('geom', ST_Intersection(_geom, _tbl.geom));
      RETURN NEXT _tbl;
   END LOOP;
END
$func$  LANGUAGE plpgsql;
_

コール:

_SELECT *, ST_AsText(geom) AS clipped_geom_wkt -- if you need the redundant column
FROM   my_func2(NULL::public.clipped_table, $my_clipper_geom, $my_age_sequence);
_
1