Trying to FOR LOOP a column with ST_CONTAINS in PgAdmin4 - postgresql

I am working with PgAdmin4 to create a View that consists of a large set of geometric data. Part of this data is polylines that exist within polygons. I am attempting to write a code that can loop through all of my polyline data in a given column, and check if it is in a given polygon, and return true/false. So far this is what I have.
DO
$$
BEGIN
FOR i IN (SELECT "geom" FROM "street map"."segment_id")
LOOP
SELECT ST_CONTAINS(
(SELECT "geom" FROM "street map"."cc_districts" WHERE "district number" = 1),
(i)
)
RETURN NEXT i
END LOOP;
END
$$
The error I receive when running this code is as follows:
ERROR: loop variable of loop over rows must be a record or row variable or list of scalar variables
LINE 4: FOR i IN (SELECT "geom" FROM "street map"."segment_id")
^
SQL state: 42601
Character: 18
From what I understand, "i" must refer to a "row variable", and I tried to define that variable with this piece of code:
(SELECT "geom" FROM "street map"."segment_id")
Any ideas to get this going would be very helpful.

A simple join would be much more efficient here
SELECT line.*, polygon.id IS NOT NULL AS is_in_polygon
FROM line
LEFT JOIN polygon
ON ST_Contains(polygon.geometry, line.geometry)
AND polygon.id = 1
Which can be translated as:
Get every field of a line record, and true if the polygon.id exists (is not null), false otherwise (more below). Name this boolean field is_in_polygon.
Do this on every line.
Join (link) each line to the polygon layer. If there is no match, keep the line information and put NULL for every polygon field (this is a left join). If there is a match, keep both line and polygon information.
A match is found if the polygon.geometry contains the line.geometry and if the polygon.id = 1

I have found a way to make this work without doing a JOIN or needing a FOR LOOP. The following code works.
SELECT * FROM "street map"."segment_id"
WHERE
ST_WITHIN(
ST_CENTROID((ST_SetSRID(geom, 4326))),
ST_SetSRID((SELECT geom FROM "street map"."cc_districts" WHERE "district number" = 1),4326))
This lets me do what I was intending by runing the process on all rows in a given column. I swapped from ST_CONTAINS to ST_WITHIN, and I also am now checking if the centroid of the polyline is within the given polygon by using ST_CENTROID. I found that the error goes away by asserting the SRID of the geometry to 4326 using ST_SetSRID. I'm not sure why that works, as my geoms already have an SRID of 4326.
Thanks for all of those who answered

Related

Literal SQL works: Array value must start with "{" or dimension information

I am trying to add an ARRAY to an existing jsonb ARRAY. This array will be added to the ARRAY[0] of the existing array. When I hardcode the details it's working but when I try to do it dynamically it fails with the above error. what am I doing wrong?
Postgresql 13 db server version
with whatposition as (select position pos from users cross join lateral
jsonb_array_elements(user_details->'Profile') with ordinality arr(elem,position)
where display_ok=false)
update users set user_details=jsonb_set(
user_details,concat('ARRAY[''userProfile'',''',(select pos-1 from whatposition)::text,'''',',''DocumentDetails'']')::text[],
'[{"y":"supernewValue"}]')
where display_ok=false;
SQL Error [22P02]: ERROR: malformed array literal:
"ARRAY['userProfile','0','DocumentDetails']" Detail: Array value
must start with "{" or dimension information.
This is the with subquery output.
with whatposition as (select position pos from users cross join lateral
jsonb_array_elements(user_details->'userProfile') with ordinality arr(elem,position)
where display_ok=false)
select concat('ARRAY[''userProfile'',''',(select pos-1 from whatposition)::text,'''',',''DocumentDetails'']');
OUTPUT OF THE ABOVE SQL
ARRAY['userProfile','0','DocumentDetails']
But when I pass the value as a literal to the above SQL it works just fine.
with whatposition as (select position pos from users cross join lateral
jsonb_array_elements(user_details->'userProfile') with ordinality arr(elem,position)
where display_ok=false)
update users set user_details=jsonb_set(
user_details,ARRAY['userProfile','0','DocumentDetails'],'[{"y":"cccValue"}]')
where display_ok=false;
You shouldn't put the ARRAY[…] syntax in a literal value.
with whatposition as (
select position pos
from users
cross join lateral jsonb_array_elements(user_details->'Profile') with ordinality arr(elem,position)
where display_ok=false
)
update users
set user_details=jsonb_set(
user_details,
ARRAY['userProfile', (select pos-1 from whatposition)::text, 'DocumentDetails'],
'[{"y":"supernewValue"}]'
)
where display_ok=false;
The query you are trying is broken beyond the superficial syntax error (which is addressed by Bergi).
If the CTE returns multiple rows (as expected), the ARRAY constructor will fail because the nested subselect is only allowed to return a single value in this place.
To "upsert" (insert or update) the property "DocumentDetails": [{"y": "cccValue"}]} to the first element (the one with subscript 0) of the nested JSON array user_details->'userProfile':
Postgres 14 or later
Make use of JSONB subscripting:
UPDATE users
SET user_details['userProfile'][0]['DocumentDetails'] = '[{"y":"cccValue"}]'
WHERE display_ok = FALSE;
Postgres 13
Use jsonb_set() - exactly like you already have in your last code example, only without the unneeded CTE:
UPDATE users
SET user_details = jsonb_set(user_details, '{userProfile, 0, DocumentDetails}', '[{"y":"cccValue"}]')
WHERE display_ok = FALSE;
db<>fiddle here

Substring last 3 characters of an id of an object within an area

I am intending to grab the last three characters from an id. In my code, you can see that I am using ST_WITHIN() to get an object within another object. I am then grabbing the "node_id" of all objects within that area. Below is the code:
SELECT SUBSTRING ((
SELECT "node_id" from sewers.structures
WHERE(
ST_WITHIN(
ST_CENTROID((ST_SetSRID(structures.geom, 4326))),
ST_SetSRID((SELECT geom FROM sewers."Qrtr_Qrtr_Sections" WHERE "plat_page" = '510C'),4326)) )),5,3)
This portion of the code works without issue:
SELECT "node_id" from sewers.structures
WHERE(
ST_WITHIN(
ST_CENTROID((ST_SetSRID(structures.geom, 4326))),
ST_SetSRID((SELECT geom FROM sewers."Qrtr_Qrtr_Sections" WHERE "plat_page" = '510C'),4326)) )
But when I run the SELECT SUBSTRING() on the selection, I get the following error:
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
The substring function should be called on each element, not on the entire query:
SELECT SUBSTRING("node_id",5,3)
FROM sewers.structures
WHERE ST_WITHIN ...

Why do I get wrong submission of my query?

For an assignment I have to do the following.
Write a script that safely checks whether a certain region exists:
Declare a custom region #region called Space, of type NVARCHAR(25).
Use IF NOT EXISTS, ELSE, and BEGIN..END to:
throw an error with THROW 50001, 'Error!', 0 if no record whose RegionDescription matches #region exists.
select all columns for that region from the Region table if the record does exist.
Notes:
Specify the Region table as Region, not dbo.Region.
Use SELECT * FROM Region <fill in> everywhere.
The query that I wrote is somehow not correct, but I do not know what is wrong:
DECLARE #region NVARCHAR(25)='Space'
IF NOT EXISTS (SELECT * FROM Region WHERE RegionDescription = #region)
BEGIN
THROW 50001, 'Error!', 0;
END
ELSE
BEGIN
(SELECT * FROM Region WHERE RegionDescription = #region)
END
This is the query I wrote and was successful in submission. Not sure what is different between the two....
DECLARE #region NVARCHAR(25)='Space'
IF NOT EXISTS (Select * From Region WHERE RegionDescription=#region)
BEGIN
THROW 50001,'error!',0;
END
ELSE
BEGIN
(SELECT * from Region Where RegionDescription=#region)
END
I think your query is correct. It outputs "Error!" because that is what it is supposed to do if the Region called "Space" does not exist in the table. Try to insert a row in the table with a RegionDescription called "Space", then your query should output that row.
Maybe the problem is simply that you do not have a table called Region. If this is true, then you should start by creating this table. The table must have some columns, of which one column should be called RegionDescription.
If this does not help you, could you please state the error message that you are getting.

How to check an ascending ordered column value in where clause in postgresql?

I am new to postgresql. I want to join two tables if one geometry of first table is contained by the geometry of second table. So, I have written and executed this part of the query as following and it is running fine.
select edge.start_id, cls.gid
from edge_table edge
inner join cluster_info cls on st_contains(cls.geom,st_setsrid(edge.start_geom,3067));
But it is giving the start_id and its containing geom id (as mentioned cls.gid in the query) in a random order such as following:
start_id gid
26040 2493
43323 2490
26208 2400
42754 2433
43537 2434
1379 2434
43570 2904
42887 2475
43689 2495
43211 2904
But I need to insert the result in another column named start_cls in my edge table. I need to identify the row where the cls.gid should be inserted. So, I need to check the value of start_id for each row and the cls.gid corresponding to that start_id should be put in that row. Assume, four rows of my edge table are following:
gid start_id end_id start_geom end_geom start_cls end_cls
1 81608 81608 01010000007368912D8B622341E5D022EBEAF65A41 01010000007368912D8B622341E5D022EBEAF65A41
2 81557 81520 010100000085EB51F89C0723418B6CE7DB9F8E5A41 0101000000986E1203DE0723416DE7FB51A38E5A41
3 189898 80812 01010000006F1283C0A093214179E926F1A1005B41 0101000000BE9F1A6FF3942141022B871EEC005B41
4 80952 80476 0101000000666666E67F832341F2D24DBA38B45A41 0101000000736891EDB48423413BDF4F755AB45A41
I need to fill the start_cls column first. So, the cls.gid value of 81608 (first start_id) should be there at first row under start_cls column. So, I have given one where clause as following:
select edge.start_id, cls.gid
from edge_table edge
inner join cluster_info cls on st_contains(cls.geom,st_setsrid(edge.start_geom,3067))
where (select start_id from edge_table) = edge.start_id;
But, it is giving following error:
ERROR: more than one row returned by a subquery used as an expression
********** Error **********
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
I tried with the following query too but no luck.
select edge.start_id, cls.gid
from edge_table edge
inner join cluster_info cls on st_contains(cls.geom,st_setsrid(edge.start_geom,3067))
where (select start
from (select start_id as start
from edge_table) as s) = edge.start_id;
Please help with this query. It has some geometry part but the main problem is in postgresql query organisation. So, I have raised this question in stackoverflow instead of gis.stackexchange.

What does a column assignment using an aggregate in the columns area of a select do?

I'm trying to decipher another programmer's code who is long-gone, and I came across a select statement in a stored procedure that looks like this (simplified) example:
SELECT #Table2.Col1, Table2.Col2, Table2.Col3, MysteryColumn = CASE WHEN y.Col3 IS NOT NULL THEN #Table2.MysteryColumn - y.Col3 ELSE #Table2.MysteryColumn END
INTO #Table1
FROM #Table2
LEFT OUTER JOIN (
SELECT Table3.Col1, Table3.Col2, Col3 = SUM(#Table3.Col3)
FROM Table3
INNER JOIN #Table4 ON Table4.Col1 = Table3.Col1 AND Table4.Col2 = Table3.Col2
GROUP BY Table3.Col1, Table3.Col2
) AS y ON #Table2.Col1 = y.Col1 AND #Table2.Col2 = y.Col2
WHERE #Table2.Col2 < #EnteredValue
My question, what does the fourth column of the primary selection do? does it produce a boolean value checking to see if the values are equal? or does it set the #Table2.MysteryColumn equal to some value and then inserts it into #Table1? Or does it just update the #Table2.MysteryColumn and not output a value into #Table1?
This same thing seems to happen inside of the sub-query on the third column, and I am equally at a loss as to what that does as well.
MysteryColumn = gives the expression a name also called a column alias. The fact that a column in the table#2 also has the same name is besides the point.
Since it uses INTO syntax it also gives the column its name in the resulting temporary table. See the SELECT CLAUSE and note | column_alias = expression and the INTO CLAUSE