动态定义一个RETURN表(列类型,名称)用于后续循环

use*_*583 5 postgresql dynamic-sql plpgsql postgis

我想对ST_Intersection(clipper_geom, clipped_geom)来自表的行数执行。

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

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

POSTGIS 交集本身不支持处理多个几何图形,这与ST_Intersects()我必须设计一个函数(返回一个表)来选择与我的clipper_geomusing相交的行ST_Intersects()、循环遍历结果集并计算与 的每个交集有关ST_Intersection()。字段geomclipped_geom_wkt是记录剪裁几何体的字段。

该功能有效,但我需要为每个要生成剪辑的表使用不同的功能。我想动态读取输入表(列名称和类型)并在RETURN语句中定义两者。

所有字段名称和类型都相同,只是geom更新和clipped_geom_wkt添加。

我尝试搜索 Stack Overflow,找到了有关如何创建动态表结构的示例,但没有一个LOOP对第一个结果执行后续操作,其中列名必须匹配才能插入/更新新数据。

这是我到目前为止所提出的,但我不确定如何执行该LOOP部分、添加clipped_geom_wkt字段和更新geom字段。一些回复建议RETURNS TABLE (...)如果添加更多字段SETOF...

/sf/ask/1247503771/

但似乎动态生成的列仅支持RETURNS SETOF ...

/sf/ask/821817951/#11751557

/sf/ask/602362211/#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'
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 6

你可能需要什么

您应该能够用一个简单的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;
Run Code Online (Sandbox Code Playgroud)

细微差别:您得到原始结果geom geom2结果的交集。如果您确实需要,请将多余的内容添加ST_AsText(ST_Intersection(x.geom, t.geom)) AS clipped_geom_wkt到列表中。SELECT

你问什么

我是这样理解你的问题的:

我有各种输入表,每个输入表都有一列geom geometry。我想要一个函数接受表名clipper_geom geometry作为输入并返回与我的clipper_geom. 应添加一列clipped_geom_wkt text并更改值geom,每列显示与 的交集clipper_geom

这不是小事。SQL是一种静态语言,函数最晚需要在调用时知道返回类型。我们可以使用多态类型实现动态结果,但我们需要一个众所周知的行类型来使用。现有表的行类型适合它,但您想附加另一列。这并不容易实现。解决方法是返回多态行类型,加上一个额外的列(就像您似乎已经尝试过一样)。分解函数调用中的行类型以获得所需的结果:

CREATE OR REPLACE FUNCTION my_func(_tbl ANYELEMENT
                                 , _clipper_geom text
                                 , _age_sequence text)
  RETURNS TABLE (tbl ANYELEMENT, clipped_geom_wkt text)
  LANGUAGE plpgsql 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, _geom  -- data types must match!
   LOOP          
      tbl := json_populate_record(tbl, json_build_object('geom', ST_Intersection(_geom, tbl.geom)));
      clipped_geom_wkt := ST_AsText(tbl.geom);
      RETURN NEXT;
   END LOOP;
END
$func$
Run Code Online (Sandbox Code Playgroud)

致电(重要!):

SELECT (tbl).*, clipped_geom_wkt
FROM my_func(NULL::public.clipped_table, $my_clipper_geom, $my_age_sequence);
Run Code Online (Sandbox Code Playgroud)

在 Postgres 10 中为我工作。

显着特点

更简单

我坚持你上面的设计,但对于给定的例子来说这是不必要的复杂化。额外的列完全是多余的,只需从函数调用中clipped_geom_wkt派生即可。geom然后我们就可以直接使用输入类型了:

CREATE OR REPLACE FUNCTION my_func2(_tbl ANYELEMENT
                                  , _clipper_geom text
                                  , _age_sequence text)
  RETURNS SETOF ANYELEMENT
  LANGUAGE plpgsql 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, _geom  -- data types must match!
   LOOP
      _tbl := json_populate_record(_tbl, json_build_object('geom', ST_Intersection(_geom, _tbl.geom)));
      RETURN NEXT _tbl;
   END LOOP;
END
$func$;
Run Code Online (Sandbox Code Playgroud)

致电(重要!):

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);
Run Code Online (Sandbox Code Playgroud)