最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

sql - Trigger update on PostgresqlPostGIS table - Stack Overflow

programmeradmin2浏览0评论

I am working in a Postgresql database with PostGIS.

I need to automatically populate a PostGIS column with the intersection of another column's geometry and a landmass polygon. The current trigger function is not populating the column, returning NULL.

I expect: having the column "croppedtolandmass" filled with a single geometry of the intersection of the geometry column and my landmasses05deg grid.

I am getting: only NULLs in "croppedtolandmass".

No warning is raised.

All of the columns exists.

The table I want to update is defined like this:

CREATE TABLE IF NOT EXISTS public."MOBI_site"
(
    "verbatimSiteID" text COLLATE pg_catalog."default" NOT NULL,
    "datasetID" integer NOT NULL,
    "footprintSRS" text COLLATE pg_catalog."default" NOT NULL,
    "verbatimFootprintSRS" text COLLATE pg_catalog."default" NOT NULL,
    geometry geometry,
    croppedtolandmass geometry, --This is what I want to estimate on every update
    CONSTRAINT "MOBI_site_pkey" PRIMARY KEY ("verbatimSiteID", "datasetID"),
    CONSTRAINT "MOBI_site_MOBI_dataset_FK" FOREIGN KEY ("datasetID")
        REFERENCES public."MOBI_dataset" ("datasetID") MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE
)

My function and trigger code the following:

CREATE OR REPLACE FUNCTION update_cropped_geometry()
RETURNS TRIGGER AS $$
BEGIN
    NEW.croppedtolandmass := ST_Intersection(
        NEW.geometry::geography,
        (SELECT ST_Union(geom) FROM wrld_grids.landmass05deg 
         WHERE ST_Intersects(ST_Envelope(NEW.geometry), ST_Envelope(geom))
         AND ST_Intersects(NEW.geometry, geom)
        )::geography
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_update_cropped_geometry
BEFORE UPDATE ON public."MOBI_site"
FOR EACH ROW
EXECUTE FUNCTION update_cropped_geometry();

I will appreciate any hint to discover what am I doing wrong.

I am working in a Postgresql database with PostGIS.

I need to automatically populate a PostGIS column with the intersection of another column's geometry and a landmass polygon. The current trigger function is not populating the column, returning NULL.

I expect: having the column "croppedtolandmass" filled with a single geometry of the intersection of the geometry column and my landmasses05deg grid.

I am getting: only NULLs in "croppedtolandmass".

No warning is raised.

All of the columns exists.

The table I want to update is defined like this:

CREATE TABLE IF NOT EXISTS public."MOBI_site"
(
    "verbatimSiteID" text COLLATE pg_catalog."default" NOT NULL,
    "datasetID" integer NOT NULL,
    "footprintSRS" text COLLATE pg_catalog."default" NOT NULL,
    "verbatimFootprintSRS" text COLLATE pg_catalog."default" NOT NULL,
    geometry geometry,
    croppedtolandmass geometry, --This is what I want to estimate on every update
    CONSTRAINT "MOBI_site_pkey" PRIMARY KEY ("verbatimSiteID", "datasetID"),
    CONSTRAINT "MOBI_site_MOBI_dataset_FK" FOREIGN KEY ("datasetID")
        REFERENCES public."MOBI_dataset" ("datasetID") MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE
)

My function and trigger code the following:

CREATE OR REPLACE FUNCTION update_cropped_geometry()
RETURNS TRIGGER AS $$
BEGIN
    NEW.croppedtolandmass := ST_Intersection(
        NEW.geometry::geography,
        (SELECT ST_Union(geom) FROM wrld_grids.landmass05deg 
         WHERE ST_Intersects(ST_Envelope(NEW.geometry), ST_Envelope(geom))
         AND ST_Intersects(NEW.geometry, geom)
        )::geography
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_update_cropped_geometry
BEFORE UPDATE ON public."MOBI_site"
FOR EACH ROW
EXECUTE FUNCTION update_cropped_geometry();

I will appreciate any hint to discover what am I doing wrong.

Share Improve this question asked 2 days ago Gabriel Ortega-SolisGabriel Ortega-Solis 211 bronze badge New contributor Gabriel Ortega-Solis is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 7
  • 1 Why the cast to geography? Maybe one of the geographies is invalid (it is possible that the geometry is ok but not the geography). Also beware of st_intersection using geographies, as they are first converted to the "best" possible projection, which can be surprisingly bad for large areas ("code") – JGH Commented 2 days ago
  • Are all involved geometries in the same CRS 4326? (if so, it would be safer to add this information in the column definition, i.e geometry(geometry,4326)) – JGH Commented 2 days ago
  • 1 You can try to run the query outside of the trigger, part by part, and see where it is not returning the expected result – JGH Commented 2 days ago
  • Please add sample data. As a side-note: the envelope intersection is redundant. Intersection of base geometries directly implies intersection of their envelopes On top of that, the ST_Intersects() function is inlineable so you can run your query through explain verbose and see there's a && operation on bboxes in it already. Except a missing cast back to geometry, there's nothing inherently wrong about the code you showed so I'd guess it's something about your data. – Zegarek Commented 2 days ago
  • If I make my landmasses small and scarce, most of my MOBI_site geometries can't find anything to intersect with, so the update will set croppedtolandmass to null. An intersection with nothing, is nothing: demo. – Zegarek Commented 2 days ago
 |  Show 2 more comments

1 Answer 1

Reset to default 2

Except a missing cast back to geometry, there's nothing inherently wrong about the code you showed so I'd guess it's something about your data.

If I make my landmass05deg small and scarce, most of my MOBI_site geometries can't find anything to intersect with, so the update will set croppedtolandmass to null. An intersection with nothing, is nothing.

To clarify: the trigger must be before update or insert in order to fire when you populate the table via insert, merge..then insert or copy.
A plain before update obviously only fires on update, merge..then update and insert..on conflict do update.

If the trigger was defined after you pulled in your data, you need to run an unqualified update to make it fire for all existing rows. It might be a good idea to compare counts before and after, to see if it really changed nothing, or just very little.
demo at db<>fiddle

select count(*)
     , count(*)filter(where croppedtolandmass is null) as nulls
from public."MOBI_site";
count nulls
10000 10000
explain analyze verbose update public."MOBI_site" set croppedtolandmass=null;

The trigger intercepts it and overwrites that value everywhere (as long as your landmasses actually intersect your geometries):

QUERY PLAN
Update on public."MOBI_site" (cost=0.00..1327.99 rows=0 width=0) (actual time=1029.321..1029.321 rows=0 loops=1)

  -> Seq Scan on public."MOBI_site" (cost=0.00..1327.99 rows=30599 width=38) (actual time=0.171..8.603 rows=10000 loops=1)

        Output: NULL::geometry, ctid

Planning Time: 0.057 ms
Trigger trg_update_cropped_geometry: time=648.111 calls=10000
Execution Time: 1029.361 ms
select count(*)
     , count(*)filter(where croppedtolandmass is null) as nulls
from public."MOBI_site";
count nulls
10000 9491

Side-note: the envelope intersection is redundant. Intersection of base geometries directly implies intersection of their envelopes On top of that, the ST_Intersects() function is inlineable so you can run your query through EXPLAIN VERBOSE and see there's a && operation on bboxes in it already:

explain analyze verbose
update public."MOBI_site" as t
set croppedtolandmass=ST_Intersection(
        t.geometry::geography,
        (SELECT ST_Union(geom) FROM wrld_grids.landmass05deg 
         WHERE ST_Intersects(ST_Envelope(t.geometry), ST_Envelope(geom))
         AND ST_Intersects(t.geometry, geom)
        )::geography
    )::geometry;
QUERY PLAN
Update on public."MOBI_site" t (cost=0.00..779800.91 rows=0 width=0) (actual time=1201.927..1201.928 rows=0 loops=1)

  -> Seq Scan on public."MOBI_site" t (cost=0.00..779800.91 rows=9315 width=38) (actual time=0.080..274.986 rows=10000 loops=1)

        Output: (st_intersection((t.geometry)::geography, ((SubPlan 1))::geography))::geometry, t.ctid

        SubPlan 1

          -> Aggregate (cost=83.40..83.41 rows=1 width=32) (actual time=0.007..0.007 rows=1 loops=10000)

                Output: st_union(landmass05deg.geom)

                -> Index Scan using landmass05deg_geom_idx on wrld_grids.landmass05deg (cost=0.13..58.40 rows=1 width=184) (actual time=0.005..0.005 rows=0 loops=10000)

                      Output: landmass05deg.geom

                      Index Cond: (landmass05deg.geom && t.geometry)

                      Filter: (st_intersects(t.geometry, landmass05deg.geom) AND st_intersects(st_envelope(t.geometry), st_envelope(landmass05deg.geom)))

                      Rows Removed by Filter: 0

Trigger trg_update_cropped_geometry: time=597.742 calls=10000
explain analyze verbose
update public."MOBI_site" as t
set croppedtolandmass=ST_Intersection(
        t.geometry::geography,
        (SELECT ST_Union(geom) FROM wrld_grids.landmass05deg 
         WHERE ST_Intersects(t.geometry, geom)
        )::geography
    )::geometry;
QUERY PLAN
Update on public."MOBI_site" t (cost=0.00..1089194.32 rows=0 width=0) (actual time=1245.480..1245.481 rows=0 loops=1)

  -> Seq Scan on public."MOBI_site" t (cost=0.00..1089194.32 rows=18630 width=38) (actual time=0.771..283.106 rows=10000 loops=1)

        Output: (st_intersection((t.geometry)::geography, ((SubPlan 1))::geography))::geometry, t.ctid

        SubPlan 1

          -> Aggregate (cost=58.15..58.16 rows=1 width=32) (actual time=0.007..0.007 rows=1 loops=10000)

                Output: st_union(landmass05deg.geom)

                -> Index Scan using landmass05deg_geom_idx on wrld_grids.landmass05deg (cost=0.13..33.15 rows=1 width=184) (actual time=0.005..0.005 rows=0 loops=10000)

                      Output: landmass05deg.geom

                      Index Cond: (landmass05deg.geom && t.geometry)

                      Filter: st_intersects(t.geometry, landmass05deg.geom)

                      Rows Removed by Filter: 0

Trigger trg_update_cropped_geometry: time=604.371 calls=10000
发布评论

评论列表(0)

  1. 暂无评论