Subquery returning multiple rows during update - oracle

I have two tables T_SUBJECTS (subject_id, date_of_birth) and T_ADMISSIONS (visit_id, subject_id, date_of_admission, age). I want to update the age column with the age at time of admission. I wrote the update query and get the "single row sub-query returns more than one row". I understand the error but thought the where exists clause will solve the problem. Below is the query.
UPDATE
t_admissions
SET
t_admissions.age =
(
SELECT
TRUNC(months_between(t_admissions.date_of_admission,
t_subjects.date_of_birth)/12)
FROM
t_admissions,
t_subjects
WHERE
t_admissions.subject_id = t_subjects.subject_id
AND t_admissions.age = 0
AND t_admissions.date_of_admission IS NOT NULL
AND t_subjects.date_of_birth IS NOT NULL
)
WHERE
EXISTS
(
SELECT
1
FROM
t_admissions, t_subjects
WHERE
t_admissions.subject_id = t_subjects.subject_id
);

The problem is that your subquery in the SET clause returns multiple rows.
Having a WHERE clause will only filter which records get updated and nothing else.
In addition, your where clause will either always return true or always return false.
You should look into how to properly do a correlated update:
https://stackoverflow.com/a/7031405/477563

A correlated update is what I need as suggested in the above link. See answer below.
UPDATE
(
SELECT
t_admissions.visit_id,
t_admissions.date_of_admission doa,
t_admissions.age age,
t_subjects.date_of_birth dob
FROM
t_admissions,
t_subjects
WHERE
t_admissions.subject_id = t_subjects.subject_id
AND t_admissions.age = 0
AND t_admissions.date_of_admission IS NOT NULL
AND t_subjects.date_of_birth IS NOT NULL
)
SET
age = TRUNC(months_between(doa,dob)/12);

Related

Trying to update a field in Oracle based on a select statement - getting subquery returns more than one row error

I am trying to update a field on a table the query needs to get the value from a 2nd table and use that to get the data from a 3rd table. I keep getting the "ORA-01427: single-row subquery returns more than one row" Any help is appreciated.
update ord_detail set cuser1 = (select c.email from contact c,ord_detail m join orders o on o.ID = m.orders_ID where c.email is not null)
where EXISTS (select email from contact,orders where orders.contact_id2 = contact.id)
Since you did not provide further information like the results of your sub selects, it's not 100% clear what exactly is your problem. You should check those results and if this does not answer your question, please provide the details.
My guess is that this sub query will give multiple rows because you are missing a join from the table "contact" to one of the other tables:
select c.email from contact c,ord_detail m
join orders o on o.ID = m.orders_ID where c.email is not null
Therefore, this sub query will always lead to many rows as result unless the table "contact" contains one row only whose column email is not null.

UPDATE TABLE T1 SET COLUMN= SELECT 0 FROM TABLE T1

UPDATE SCPOMGR.DFUVIEW D
SET D.UDC_NEWDFU = (SELECT (CASE WHEN D1.UDC_CREATIONDATE > ADD_MONTHS (TRUNC(SYSDATE),3)
THEN 1
ELSE 0
END)
FROM SCPOMGR.DFUVIEW D1, SCPOMGR.UDT_GEN_PARAM G
WHERE D.DMDUNIT = D1.DMDUNIT
AND D.DMDGROUP = D1.DMDGROUP
AND D.LOC = D1.LOC
AND G.REGION=VREGION
AND G.JDA_CODE=SUBSTR(D1.DMDUNIT,-2,2)
);
WHAT WILL A SELECT 0 DO?
WILL IT SELECT NO ROWS AND UPDATE THE COLUMN WITH A NULL?
On the right-hand side of the assignment (the value you want to UPDATE) you have a SELECT statement with a CASE expression. There is a subquery (a join with several additional filters). The SELECT statement will return exactly as many rows as there are in the join and satisfy all the additional filters. For some of those rows the SELECT statement will return 1 and for the others, 0.
Do you expect that the subquery (the join with many WHERE conditions) will return exactly one row, then you check the CREATIONDATE and based on that, you update the value to 1 or 0?
In principle, the SELECT statement may return more than one row - in which case you will get a different error message, something about a scalar query (one that is supposed to be scalar, anyway) returning more than one row.
It is also possible that the subquery returns no rows at all - in that case the SELECT statement will return no rows at all, and in such cases the UPDATE means "assign NULL to the column of the left-hand side of the assignment". This is what seems to have happened here.
That is no reason to raise an exception. However, you would get an exception if the column (UDC_NEWDFU) has a NOT NULL constraint.
Since the CASE expression can ONLY return 0 or 1, never NULL, the ONLY way where a NULL assignment is possible is when the subquery (the join with many conditions) returns no rows. In one of the Comments you said that subquery does return rows. I don't believe you. If it does return rows, then I don't believe you that you have an update to NULL being attempted.

Update statement with joins in Oracle

I need to update one column in table A with the result of a multiplication of one field from table A with one field from table B.
It would be pretty simple to do this in T-SQL, but I can't write the correct syntax in Oracle.
What I've tried:
UPDATE TABLE_A
SET TABLE_A.COLUMN_TO_UPDATE =
(select TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID)
WHERE TABLE_A.MONTH_ID IN (201601, 201602, 201603);
But I keep getting errors. Could anybody help me, please?
I generally prefer to use the below format for such cases since this will ensure there's no update performed if there's no data in the table(query extracted temp table) whereas in the above solution provided by Brian Leach will update the new value as null if there's no record present in the 2nd table but exists in the first table.
UPDATE
(
select TABLE_A.COLUMN_TO_UPDATE
, TABLE_A.PRODUCT_ID
, TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE as value
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID
AND TABLE_A.MONTH_ID IN (201601, 201602, 201603)
) DATA
SET DATA.COLUMN_TO_UPDATE = DATA.value;
This solution can cause key preserved value issues which shouldn't be an issue here since i expect a single row in both the tables for one product(ID).
More on Key Preserved table concept in inner join can be found here
https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:548422757486
#Jayesh Mulwani raiesed a valid point, this will set the value to null if there is no matching record. This may or may not be the desired result. If it isn't, and no change is desirect, you can change the select statement to:
coalesce((SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id AND table_a.sales_channel_id = table_b.sales_channel_id),1)
If this is the desired outcome, Jayesh's solution will be more efficient as it will only update matching records.
UPDATE table_a
SET table_a.column_to_update = table_a.column_with_some_value
* (SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id
AND table_a.sales_channel_id = table_b.sales_channel_id)
WHERE table_a.month_id IN (201601, 201602, 201603);

Update a column based on column from another table

Why does my update statement say - "cannot insert null into gitb_auto_debit_upload.status". The second query returned a record where the column process_status was having 'P' as its value.
Update gitb_auto_debit_upload a
set status = (select nvl(process_status,'O') from gitb_daily_log b
where b.interface_code = 'PHP661OW'
and b. process_ref_no = '4708'
and a.refno = b.external_ref_no
and a.recordno = b. seq_no
) ;
select * from gitb_auto_debit_upload a, gitb_daily_log b where b.interface_code = 'PHP661OW'
and b. process_ref_no = '4708'
and a.refno = b.external_ref_no
and a. recordno = b. seq_no
The error message comes from a record in gitb_auto_debit_upload for which the select (the one inside the update) statement returns no row.
Your update statement processes all records of gitb_auto_debit_upload.
Your select (the one you use to test the status value) statement only those records for which a record in gitb_daily_log exists.
You need to change the update statement to either only update those rows for whicht the select return a row or to place the NVL() around the select.

Oracle "Cannot update to NULL"

I have this query on Oracle 10g:
UPDATE "SCHEMA1"."CELLS_GLIST"
SET ("GLIST_VALUE_ID", "USER_ID", "SESSION_ID") = (
SELECT "GLIST_VALUE_ID", 1 AS "USER_ID", 123456 AS "SESSION_ID"
FROM "SCHEMA1"."GLISTS_VALUES_UOR"
WHERE ("UOR_ID"=3)
AND ("GLIST_ID"=67)
AND ("GLIST_VALUE_DESC" = (
SELECT "GLIST_VALUE_DESC"
FROM "BMAN_TP1"."GLISTS_VALUES_UOR"
WHERE ("UOR_ID"=3)
AND ("GLIST_VALUE_ID"="CELLS_GLIST"."GLIST_VALUE_ID")
))
)
WHERE EXISTS (......)
It keeps saying ORA-01407: cannot update ("SCHEMA1"."CELLS_GLIST"."SESSION_ID") to NULL
"SESSION_ID" is obviously Not Nullable, but I'm actually passing a value to that field, so I do not understand the problem.
From your comments, I read that you seem to want to write a default record to your target table, in case the subquery doesn't return any records. So the correct way to phrase your query would be using a MERGE statement as such:
MERGE INTO "SCHEMA1"."CELLS_GLIST" dst
USING (
-- rephrase your subquery here. This is your "merge data source". The number
-- of records returned in this subquery will correspond to the number of
-- affected records in dst
) src
ON (
-- the missing exists condition here. Everytime this condition matches a record
-- between dst and src, an UPDATE is performed. Otherwise, an INSERT is
-- performed
)
WHEN MATCHED THEN UPDATE
SET dst."GLIST_VALUE_ID" = src."GLIST_VALUE_ID"
WHEN NOT MATCHED THEN INSERT ("GLIST_VALUE_ID", "USER_ID", "SESSION_ID")
VALUES (NULL, 1, 123456);
This is just to give you an idea. I'm not quite sure what you're trying to achieve in detail, so I omitted the subqueries and conditions
I found this query also to work:
UPDATE "BMAN_TP1"."CELLS_GLIST"
SET "GLIST_VALUE_ID" = (
SELECT "GLIST_VALUE_ID"
FROM "BMAN_TP1"."GLISTS_VALUES_UOR"
WHERE ("UOR_ID"=3)
AND ("GLIST_ID"=67)
AND ("GLIST_VALUE_DESC" = (
SELECT "GLIST_VALUE_DESC"
FROM "BMAN_TP1"."GLISTS_VALUES_UOR"
WHERE ("UOR_ID"=3)
AND ("GLIST_VALUE_ID"="CELLS_GLIST"."GLIST_VALUE_ID")
))
),
"SESSION_ID" = 123456,
"USER_ID" = 1
WHERE EXISTS (......)
But, it performs really really fast... I doubt I'm missing something...

Resources