PostGIS Intersection of multiple tables - sql

In my postGIS DB, I need to create a new layer from the intersection of multiple polygons layers, while maintaining all fields (or Columns) from all layers (or tables) ==> in the output table, I need all columns for the 3 input tables
I believe it has to include ST_Intersects, but I am unable to find the correct syntax. Can you help me designing the SQL command line, knowing the following generic table names:
- TableA (with the columns: GeomA (geometry), ColumnA1, ColumnA2)
- TableB (with the columns: GeomB (geometry), ColumnB1)
- TableC (with the columns: GeomC (geometry), ColumnC1)
All geometry fields from TableA,TableB and TableC are in the same projection.
Thanks

For clarity, since "interaction with multiple polygons layers" is a bit vague, it could mean:
you want to find all polygons from A that intersect with a polygon from B and a polygon from C
or A with B, B with C
or A with B, A with C and B with C
For simplicity let us assume the first scenario, and I presume the others will be pretty easy to deduce:
select A.*, B.*, C.*
from A, B, C
where st_intersects(A.geomA, B.geomB) = true
and st_intersects(A.geomA, C.geomC) = true
[EDIT] Not just finding the rows, but if the intersection itself is important we can do the following (in the simple case of two geometries intersecting)
select A.*, B.*, st_intersection(A.geomA, B.geomB) as geomAB
from A, B
where st_intersects(A.geomA, B.geomB) = true
I simplified the case because if A intersects with B, and A intersects with C, it is not even sure those two intersections intersect again and have a result.
If we suppost A intersects with B, B with C and C with A then we should have a intersection imho. So that would look like:
select A.*, B.*, C.*, st_intersection(A.geomA, st_intersection(B.geomB, C.geomC)) as geomABC
from A, B, C
where st_intersects(A.geomA, B.geomB) = true
and st_intersects(A.geomA, C.geomC) = true
and st_intersects(B.geomB, C.geomC) = true

Related

Python unpacking operator / unpack subquery in separated columns

In Python there is the unpacking operator (*), which allows you to take an iterator or iterable (tuple, list, generator, etc.) and pass each of its items as an argument to a function. I want to do the same thing with a Postgresql subquery. But I can't find any information anywhere.
I want to do something like this:
INSERT INTO tabla1(a, b, c) SELECT a, *(SELECT b, c FROM tabla2 LIMIT 1) FROM tabla3
To avoid doing two almost identical selects and speed up my queries.
I want to AVOID something like this:
INSERT INTO tabla1(a, b, c) SELECT a, (SELECT b FROM tabla2 LIMIT 1), (SELECT c FROM tabla2 LIMIT 1) FROM tabla3
I tried the following:
review documentation
use with statement (It doesn't work for me because I can't relate a query column to the subquery, which would be necessary for me)
Read a question from this site (I don't have the link)
My question would be, is there something similar to that in Postgresql or any way to affect multiple columns with a single subquery? For example, something like the with statement with which you can do name_of_the_query.column?
Edit
This is the query I did with with, the test query with real names, I hope it makes my question better quality.
WITH ztabla02 AS (SELECT (CASE WHEN LEFT(maecuent.cuenta, 1) IN ('1','2','3') THEN
array[(SELECT descripcio FROM ztabla02 WHERE c_tabla='PR' AND c_clave=maecuent.cod_pcia), 'ARGENTINA']
ELSE
array['', (SELECT descripcio FROM ztabla02 WHERE c_tabla='PA' AND c_clave=maecuent.cod_pcia)]
END)
AS tuple)
SELECT ztabla02.tuple[1], ztabla02.tuple[2] FROM maecuent
Error:
ERROR: falta una entrada para la tabla «maecuent» en la cláusula FROM
LINE 1: WITH ztabla02 AS (SELECT (CASE WHEN LEFT(maecuent.cuenta, 1)...
^
You can do it with a CTE
with t2 as (
SELECT b, c FROM tabla2 LIMIT 1
)
INSERT INTO tabla1(a, b, c)
SELECT t3.a, t2.b, t2.c
FROM tabla3 t3
CROSS JOIN t2
Note LIMIT without ORDER BY will return an arbitrary row.

Select only the last date when two columns are duplicate

I need to select seven columns from three different tables, only when one of the columns has a particular value. I also need to select only the last date when two columns (TAGNAME and TAGNUMMER) are both duplicate. I'm using the following code:
select c.AKEY, c.AKT_DATUM, c.TAGNAME, c.TAGNUMMER,
cd.TEILANLAGEN_ID, x.TP_GSAP_KZ, c.KLASSEN_ID
from T0EM01 c, T0EM03 x, T0AD07 cd
where cd.TEILANLAGEN_ID = '219A'
inner join
(select c.TAGNAME and c.TAGNUMMER max(C.AKT_DATUM)
where T0EM01 c c.TAGNAME and T0EM01 c c.TAGNUMMER = m.max_date
Up to where cd.TEIANLAGEN_ID = '219A' it works fine (but there are over 2 million rows).
How can I filter so that when both TAGNAME and TAGNUMMER are repeated in two or more rows I only select the latest date?
"Over 2 million rows" could be less if you properly joined those 3 tables. The way you put it, you're producing Cartesian join and got way too many rows.
from t0em01 c,
t0em03 x,
t0ad07 cd
I have no idea how are they to be joined to each other so I'm just guessing; you should know.
As of the "max date value", one option might be to use a subquery, also properly joined to other table(s). Once again, I don't know how exactly to join them.
Improve it:
select c.akey,
c.akt_datum,
c.tagname,
c.tagnummer,
cd.teilanlagen_id,
x.tp_gsap_kz,
c.klassen_id
from t0em01 c join t0em03 x on x.id = c.id --> I'm just
join t0ad07 cd on cd.id = c.id -- guessing here
where cd.teilanlagen_id = '219A'
and c.akt_datum = (select max(c1.akt_datum) --> subquery, to return
from t0em01 c1 -- only the MAX date value
where c1.tagname = c.tagname
and c1.tagnummer = c.tagnummer
);

How to find all pair of polygons which only touch each other in a point and only list each pair once

How to find all pair of polygons which only touch each other in a point and only list each pair once in PostgreSQL using PostGIS?
like the cycle shown on the picture:
I have written the following query:
with kms as (
select
a.county as cn1,
b.county as cn2
from spatial.us_counties as a, spatial.us_counties as b
where ST_Touches(a.geom, b.geom) = 'true' and a.id != b.id and ST_GeometryType(ST_Intersection(a.geom,b.geom)) = 'ST_Point'
)
/** below is for remove reversed pairs **/
SELECT t1.cn1
,t1.cn2
FROM kms AS t1
LEFT OUTER JOIN kms AS t2
ON t1.cn1 = t2.cn2
AND t1.cn2 = t2.cn1
WHERE t2.cn1 IS NULL
OR t1.cn1 < t2.cn1
But this query caused serious performance issue and it returned all pairs twice (reversed pair)
This approach is not the solution at all.
So is there anyone can help me with that or give me any hints?
I'm not absolutely sure so I need your feedback for this answer..
Try:
SELECT DISTINCT A.county
FROM spatial.us_counties AS A, spatial.us_counties AS B
WHERE ST_Touches(A.geom, B.geom) = 'true'
According to: https://postgis.net/docs/ST_Touches.html ST_Touches should return touching polygons only and not intersecting so this should eliminate the need for the where statement that checks if it's a point intersection. Selecting DISTINCT should help with the duplicates.
Adding an index https://postgis.net/docs/using_postgis_dbmanagement.html#idm2269 to the table will help speed up the geometry queries. Let me know if you've already done all this, I can edit my answer.

Oracle: outer join(+) with or clause replacement

I have an enormous select that schematically looks like this:
SELECT c_1, c_2, ..., c_j FROM t_1, t_2, ..., t_k
WHERE e_11 = e_12(+)
AND e_21 = e_22(+)
AND ...
AND e_l1 = e_l2(+)
ORDER BY o
where j, k and l are in hundreds and e_mn is a column from some table. I need to add new columns A_1 and A_2 to the select from a new table T. The new columns are connected to the former select via a column call it B from a table R. I want those rows where A_1 = B or A_2 = B or those rows where there is no correspondeing A_i to the value B.
Suppose I only had to deal with tables T and R then I want this:
SELECT * FROM R
LEFT OUTER JOIN T
ON (A_1 = B OR A_2 = B)
To mimic this behaviour I'd want something like this in the big select:
SELECT c_1, c_2, ..., c_j, A_1, A_2 FROM t_1, t_2, ..., t_k, T
WHERE e_11 = e_12(+)
AND e_21 = e_22(+)
AND ...
AND e_l1 = e_l2(+)
AND (B = A_1(+) OR B = A_2(+))
ORDER BY o
this is, however, syntactically incorrect since the (+) operator cannot be used with the OR caluse. And if I leave out the (+)'s I lose those rows where there is no corresponding A_i to B.
What are my options here? Can I somehow find a way to do this without changing the whole body of the select? I doubt there is a reasonable way to do this, nevertheless I'd appreciate any help.
Thanks.

Combining two tuples into one in oracle db

Say I have a bunch of letters grouped together and I want to find out which pair bonds with another the most, as an example I have
a b d b
b c e a
and it should return ab or ba because a-b are the most occurred here.
so far I have made a query that just pulls every two letters that are together, but when I run the query I get something like the above example but all are in separate tuples, like this
a
b
b
c
d
e
b
a
I need to compare the occurence of all the PAIRS, my logic so far is that I can use nvl() to concat them(but nvl() of a-b and b-a returns two separate instances), then run a count, but I'm not sure how to run a count on these as I called the letters
select a.value, b.value
from Letter a, Letter b, Word aw, Word bw, Sentence senA, sentence senb
where a.id = aw.aid and aw.pid = sena.id and b.id = bw.aid and
bw.pid = senb.id and aw.pid = bw.pid and a.value != b.value
;
TL;DR: I want to do a count(a.ltr-b.ltr pair) but not sure how to code that.
Thanks!
EDIT: table structure:
letter(id, value)
\
word(aid, pid)
\
sentence(id, name,sid)
basically, if two letters end up in the same sentence.id, they are a pair(bond).