more than one row returned by a subquery used as an expression in postgreSql query with IN clause - postgresql

I am facing an issue with the query in postgreSql. Below is the query.
UPDATE t_e20so1_fieldrulethen AS fthen
SET c_thenfieldid = t1.c_fieldschemaid
FROM t_sys_fieldschema AS t1
WHERE fthen.c_lyrathenfieldid = t1.c_lyraid
AND fthen.c_rulefor = 5
AND t1.c_fieldtype = 18
AND t1.c_tablegroupsid IN (
CASE
WHEN fthen.c_iffieldid = t1.c_id THEN (SELECT
c_targettableid
FROM t_sys_tablegroups
WHERE c_parentid = 'c0b2f85c-bc93-466b-a54d-b1330440db98')
ELSE (SELECT c_targettableid
FROM t_sys_tablegroups
WHERE c_parentid =
'c0b2f85c-bc93-466b-a54d-b1330440db98')
END );
As per above query, i am updating t_e20so1_fieldrulethen table from t_sys_fieldschema. One of the conditions to check is t_sys_fieldschema.c_tablegroupsid should be having specific values which are in and I am fetching them from table t_sys_tablegroups.
Above query gives me error as shown below:
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
Here, if I remove case from the query (below is what I mean) it works properly.
UPDATE t_e20so1_fieldrulethen AS fthen
SET c_thenfieldid = t1.c_fieldschemaid
FROM t_sys_fieldschema AS t1
WHERE fthen.c_lyrathenfieldid = t1.c_lyraid
AND fthen.c_rulefor = 5
AND t1.c_fieldtype = 18
AND t1.c_tablegroupsid IN (SELECT c_targettableid
FROM t_sys_tablegroups
WHERE
c_parentid = 'c0b2f85c-bc93-466b-a54d-b1330440db98')
Now I have only one select query in the "IN" clause.

I've checked that kind of case statement(two nested selects) - and it cannot be done that way. You generate two separate lists in one CASE.
One list for all records where fthen.c_iffieldid = t1.c_id and the other for ELSE statement.
As I wrote many times never use nested selects in "IN" clause. It is killing performance and causing many problems. Use "EXISTS".
As your CASE seems to be redundant (both WHEN and ELSE returns same value) change it that way and it will be faster.
UPDATE t_e20so1_fieldrulethen AS fthen
SET c_thenfieldid = t1.c_fieldschemaid
FROM t_sys_fieldschema AS t1
WHERE fthen.c_lyrathenfieldid = t1.c_lyraid
AND fthen.c_rulefor = 5
AND t1.c_fieldtype = 18
AND exists (select from t_sys_tablegroups t2
where t1.c_tablegroupsid=t2.c_targettableid
and c_parentid = 'c0b2f85c-bc93-466b-a54d-b1330440db98');

Related

UPDATE in postgresql with JOINS

I have a form where some input has a value that is made with this query:
SELECT e.tree.nombre
FROM d.p
JOIN e.theme ON id = id_capa
LEFT JOIN e.tree ON e.theme.id_tree = e.tree.id
WHERE id_capa = 816
e and d are schemas. The id_capa = 816 is passed as an argument to the query from the form that I'm editing. It returns a value correctly. Now I want to edit that value on my form, so I need to UPDATE but I have multiple tables, I read that I can't do an UPDATE with JOINS, how should I do that UPDATE?
In your original SQL query, the table d.p is not used, neither in SELECT nor in conditions. So it can be skipped; the query can be rewritten as:
SELECT e.tree.nombre
FROM e.theme
LEFT JOIN e.tree ON e.theme.id_tree = e.tree.id
WHERE e.theme.id = 816
Since the query selects values from table joined with LEFT JOIN, the result can be NULL, ie joined record from e.tree table can be missed. In this case there is nothing to update.
Existing matching record can be updated with the query:
UPDATE e.tree
SET nombre = <NEW_VALUE>
FROM e.theme
WHERE e.theme.id = 816 AND e.theme.id_tree = e.tree.id

Postgres Update Using Select Passing In Parent Variable

I need to update a few thousand rows in my Postgres table using the result of a array_agg and spatial lookup.
The query needs to take the geometry of the parent table, and return an array of the matching row IDs in the other table. It may return no IDs or potentially 2-3 IDs.
I've tried to use an UPDATE FROM but I can't seem to pass into the subquery the parent table geom column for the SELECT. I can't see any way of doing a JOIN between the 2 tables.
Here is what I currently have:
UPDATE lrc_wales_data.records
SET lrc_array = subquery.lrc_array
FROM (
SELECT array_agg(wales_lrcs.gid) AS lrc_array
FROM layers.wales_lrcs
WHERE st_dwithin(records.geom_poly, wales_lrcs.geom, 0)
) AS subquery
WHERE records.lrc = 'nrw';
The error I get is:
ERROR: invalid reference to FROM-clause entry for table "records"
LINE 7: WHERE st_dwithin(records.geom_poly, wales_lrcs.geom, 0)
Is this even possible?
Many thanks,
Steve
Realised there was no need to use SET FROM. I could just use a sub query directly in the SET:
UPDATE lrc_wales_data.records
SET lrc_array = (
SELECT array_agg(wales_lrcs.gid) AS lrc
FROM layers.wales_lrcs
WHERE st_dwithin(records.geom_poly, wales_lrcs.geom, 0)
)
WHERE records.lrc = 'nrw';

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

comprare aggregate sum function to number in postgres

I have the next query which does not work:
UPDATE item
SET popularity= (CASE
WHEN (select SUM(io.quantity) from item i NATURAL JOIN itemorder io GROUP BY io.item_id) > 3 THEN TRUE
ELSE FALSE
END);
Here I want to compare each line of inner SELECT SUM value with 3 and update popularity. But SQL gives error:
ERROR: more than one row returned by a subquery used as an expression
I understand that inner SELECT returns many values, but can smb help me in how to compare each line. In other words make loop.
When using a subquery you need to get a single row back, so you're effectively doing a query for each record in the item table.
UPDATE item i
SET popularity = (SELECT SUM(io.quantity) FROM itemorder io
WHERE io.item_id = i.item_id) > 3;
An alternative (which is a postgresql extension) is to use a derived table in a FROM clause.
UPDATE item i2
SET popularity = x.orders > 3
FROM (select i.item_id, SUM(io.quantity) as orders
from item i NATURAL JOIN itemorder io GROUP BY io.item_id)
as x(item_id,orders)
WHERE i2.item_id = x.item_id
Here you're doing a single group clause as you had, and we're joining the table to be updated with the results of the group.

PostgreSQL and pl/pgsql SYNTAX to update fields based on SELECT and FUNCTION (while loop, DISTINCT COUNT)

I have a large database, that I want to do some logic to update new fields.
The primary key is id for the table harvard_assignees
The LOGIC GOES LIKE THIS
Select all of the records based on id
For each record (WHILE), if (state is NOT NULL && country is NULL), update country_out = "US" ELSE update country_out=country
I see step 1 as a PostgreSQL query and step 2 as a function. Just trying to figure out the easiest way to implement natively with the exact syntax.
====
The second function is a little more interesting, requiring (I believe) DISTINCT:
Find all DISTINCT foreign_keys (a bivariate key of pat_type,patent)
Count Records that contain that value (e.g., n=3 records have fkey "D","388585")
Update those 3 records to identify percent as 1/n (e.g., UPDATE 3 records, set percent = 1/3)
For the first one:
UPDATE
harvard_assignees
SET
country_out = (CASE
WHEN (state is NOT NULL AND country is NULL) THEN 'US'
ELSE country
END);
At first it had condition "id = ..." but I removed that because I believe you actually want to update all records.
And for the second one:
UPDATE
example_table
SET
percent = (SELECT 1/cnt FROM (SELECT count(*) AS cnt FROM example_table AS x WHERE x.fn_key_1 = example_table.fn_key_1 AND x.fn_key_2 = example_table.fn_key_2) AS tmp WHERE cnt > 0)
That one will be kind of slow though.
I'm thinking on a solution based on window functions, you may want to explore those too.