Update value from a select statement - oracle

I'm using an Access over Oracle database system (Basically using Access for the forms and getting into the tables using ADO code) and am trying to update a field in the product table with the value of the same named field in a load table.
The code I am using is:
.CommandText = "UPDATE " & strSchema & ".TBL_CAPITAL_MGMT_PRODUCT a INNER JOIN " & strSchema & ".TBL_CAPITAL_MGMT_TEMP_LOAD b ON a.AR_ID = b.AR_ID SET a.TOT_RWA_AMT = b.TOT_RWA_AMT;"
Which returns an error about missing SET keyword.. So I changed it to:
.CommandText = "UPDATE (SELECT a.TOT_RWA_AMT, b.TOT_RWA_AMT As New_RWA_AMT FROM " & strSchema & ".TBL_CAPITAL_MGMT_TEMP_LOAD a INNER JOIN " & strSchema & ".TBL_CAPITAL_MGMT_PRODUCT b ON b.AR_ID = a.AR_ID Where a.New_Rec <> '-1' AND a.IP_ID Is Not Null) c SET c.New_RWA_AMT = c.TOT_RWA_AMT;"
Which returns an error about non key-preserved table. the b table has a pk of AR_ID but the a table has no primary key and it probably won't be getting one, I can't update the structure of any of the tables.
I tried using the /*+ BYPASS_UJVC */ which lets the code run, but doesn't actually seem to do anything.
Anyone got any ideas where I should go from here?
Thanks
Alex

Ignoring the irrelevant ADO code, the update you are trying to do is:
UPDATE TBL_CAPITAL_MGMT_PRODUCT a
INNER JOIN
SET a.TOT_RWA_AMT = b.TOT_RWA_AMT;
This isn't supported by Oracle (though maybe this undocumented BYPASS_UJVC hint is supposed to overcome that, but I wasn't aware of it till now).
Given that your inline view version fails due to lack of constraints you may have to fall back on the traditional Oracle approach using correlated subqueries:
UPDATE TBL_CAPITAL_MGMT_PRODUCT a
SET a.TOT_RWA_AMT = (SELECT b.TOT_RWA_AMT
FROM TBL_CAPITAL_MGMT_TEMP_LOAD b
WHERE a.AR_ID = b.AR_ID
)
WHERE EXISTS (SELECT NULL
FROM TBL_CAPITAL_MGMT_TEMP_LOAD b
WHERE a.AR_ID = b.AR_ID
);
The final WHERE clause is to prevent TOT_RWA_AMT being set to NULL on any "a" rows that don't have a matching "b" row. If you know that can never happen you can remove the WHERE clause.

If you're using Oracle 10g or higher, an alternative to Tony's solution would be to use a MERGE statement with only a MATCHED clause.
MERGE INTO TBL_CAPITAL_MGMT_PRODUCT a
USING TBL_CAPITAL_MGMT_TEMP_LOAD b
ON (a.AR_ID = b.AR_ID)
WHEN MATCHED THEN
UPDATE SET a.TOT_RWA_AMT = b.TOT_RWA_AMT;

Related

Oracle trying to update a table by joining a non indexed table

I tried looking for a similar example to my problem but could not reproduce the solution to my success.
I have 2 tables, Controller and Actions.
The Actions table has the columns Step, Script, Description, Wait_Until and Ref_Code.
The Controller table can only be joined on the Action table by the Ref_Code.
The Action table cannot have a PK because for each Ref_Code there is a Step to be taken.
Im getting an error when trying to update the Controller table using a merge statement:
ORA-30926: unable to get a stable set of rows in the source tables
My merge statement is as follows:
MERGE INTO DSTETL.SHB_FTPS_CONTROLLER ftpsc
USING (SELECT DISTINCT FTPSC.SESSION_ID,
FTPSC.ORDER_DATE,
sa.step,
sa.next_step,
LAST_ACTION_TMSTMP,
SA.ACTION_SCRIPT,
sa.ref_code,
SA.WAIT_UNTIL
FROM DSTETL.SHB_FTPS_CONTROLLER ftpsc, DSTETL.SHB_ACTIONS sa
WHERE SA.REF_CODE = FTPSC.REF_CODE
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step) v1
ON (v1.REF_CODE = FTPSC.REF_CODE)
WHEN MATCHED
THEN
UPDATE SET FTPSC.LAST_ACTION_TMSTMP = CURRENT_TIMESTAMP,
ftpsc.next_step = v1.next_step,
ftpsc.curr_step = v1.STEP,
ftpsc.action_script = v1.action_script
WHERE CURRENT_TIMESTAMP >= v1.LAST_ACTION_TMSTMP + v1.WAIT_UNTIL;
COMMIT;
I tried doing this using a normal update as well but Im getting ORA-01732: data manipulation operation not legal on this view.
UPDATE (SELECT FTPSC.SESSION_ID,
FTPSC.ORDER_DATE,
FTPSC.CURR_STEP,
FTPSC.NEXT_STEP,
FTPSC.ACTION_SCRIPT,
sa.step, --New Step
sa.next_step AS "NNS", --New Next Step
FTPSC.LAST_ACTION_TMSTMP,
SA.ACTION_SCRIPT AS "NAS", --New action script
sa.ref_code,
SA.WAIT_UNTIL
FROM DSTETL.SHB_FTPS_CONTROLLER ftpsc
LEFT JOIN
DSTETL.SHB_ACTIONS sa
ON SA.REF_CODE = FTPSC.REF_CODE
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step) t
SET t.curr_step = t.step,
t.LAST_ACTION_TMSTMP = CURRENT_TIMESTAMP,
t.next_step = t."NNS",
t.action_script = t."NAS";
COMMIT;
Any advice would be appreciated, I already understand this is because the Action table has multiple Ref_Codes but REF_CODE||STEP is unique. And the output of:
SELECT DISTINCT FTPSC.SESSION_ID,
FTPSC.ORDER_DATE,
sa.step,
sa.next_step,
LAST_ACTION_TMSTMP,
SA.ACTION_SCRIPT,
sa.ref_code,
SA.WAIT_UNTIL
FROM DSTETL.SHB_FTPS_CONTROLLER ftpsc, DSTETL.SHB_ACTIONS sa
WHERE SA.REF_CODE = FTPSC.REF_CODE
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step;
Is how I want the Controller table to be updated like.
Thanks in advance.
It sounds like what you want to do is: update each row in the Controller table with the matching "next step" details from the Actions table. But your Merge statement is querying the Controller table twice, which confuses things.
Is this what you're trying to do?
MERGE INTO DSTETL.SHB_FTPS_CONTROLLER ftpsc
USING (SELECT
step,
next_step,
ACTION_SCRIPT,
ref_code,
WAIT_UNTIL
FROM DSTETL.SHB_ACTIONS
) sa
ON (sa.REF_CODE = FTPSC.REF_CODE)
WHEN MATCHED
THEN
UPDATE SET FTPSC.LAST_ACTION_TMSTMP = CURRENT_TIMESTAMP,
ftpsc.next_step = sa.next_step,
ftpsc.curr_step = sa.STEP,
ftpsc.action_script = sa.action_script
WHERE CURRENT_TIMESTAMP >= ftpsc.LAST_ACTION_TMSTMP + sa.WAIT_UNTIL
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step;
EDIT: updated query
EDIT2: So, in your original query, in the USING section you were selecting the rows in the Controller table that you wanted to update... but you never joined those rows to the Controller table from the MERGE INTO section to match them up. Having the same alias "ftpsc" just made it less clear that they're two separate objects in the query, and which one you wanted to update.
Honestly I don't really understand why Oracle won't let you update columns that appear in the USING..ON clause. It apparently works fine in SQL Server.

I have an error with trigger using if conditional and update

Executing this trigger in ORACLE I got the following error:
Table, View Or Sequence reference 'OCEX_COMI.FECHA_ASIG_GT' not allowed in this context.
This is my trigger:
create or replace trigger ocex_comi_total
before insert or update of id_gt,fecha_asig_gt on ocex_comi
for each row
begin
if ((ocex_comi.fecha_asig_gt > to_date('2018-12-15','yyyy-mm-dd')) and
(ocex_comi.fecha_asig_gt < to_date('2019-01-01','yyyy-mm-dd')))
then
update ocex_comi cm set
cm.PAGO_COM = (select uea.total_bono_especial from OCEX_UEA uea
join OCEX_GUIA_TRANSITO gt on uea.id_uea = gt.dest_id
where gt.cod_gt=cm.id_gt)
where cm.id_gt = (select gt.cod_gt from ocex_guia_transito gt JOIN
ocex_uea uea on uea.id_uea=gt.dest_id
where gt.cod_gt=cm.id_gt);
else
update ocex_comi cm set
cm.PAGO_COM = (select uea.total_x_pnp from OCEX_UEA uea
join OCEX_GUIA_TRANSITO gt on uea.id_uea = gt.dest_id
where gt.cod_gt=cm.id_gt)
where cm.id_gt = (select gt.cod_gt from ocex_guia_transito gt JOIN
ocex_uea uea on uea.id_uea=gt.dest_id
where gt.cod_gt=cm.id_gt);
end if;
end;
Well what I was trying to do is that with this trigger the column "PAGO_COM" of the table "ocex_comi" is automatically filled in from the table "ocex_uea" thanks to the column "total_bono_special" (if in case the date of the field "date_asig_gt" is included) between 15-dec to 31-dec) and if not, fill in the column "total_x_pnp" (if the date of the field "fecha_asig_gt is not between 15-dec to 31-dec.) Some idea or help with the error that comes to me, thanks.
Don't refer to the table column directly; you need to refer to the new pseudorecord:
if ((:new.fecha_asig_gt > to_date('2018-12-15','yyyy-mm-dd')) and
(:new.fecha_asig_gt < to_date('2019-01-01','yyyy-mm-dd')))
then
though I'd use date literals:
if :new.fecha_asig_gt > date '2018-12-15' and
:new.fecha_asig_gt < date '2019-01-01'
then
(Not sure if you really want >= rather than >, though.)
But you are also trying to update all rows in the table inside each branch of your logic, which doesn't sound like something you really want to do anyway, and which will cause a mutating table error at runtime if you try.
It's not really clear quite what your intent is there but I think you want something more like:
...
then
select uea.total_bono_especial
into :new.PAGO_COM
from OCEX_UEA uea
join OCEX_GUIA_TRANSITO gt on uea.id_uea = gt.dest_id
where gt.cod_gt = :new.id_gt;
else
...
and the same kind of thing in the other branch.
As those queries are so similar you could replace the if/else logic with a single query, whoch uses a case expression to decide which of the two column values to return.

Oracle - Update COUNT of rows with specific value

This question has been posted several times but I am not able to get it work. I tried the approach mentioned in Update column to the COUNT of rows for specific values in another column. SQL Server. It gives me SQLException: java.sql.SQLException: ORA-01427: single-row subquery returns more than one row error not sure what I can do.
Below is my problem
UPDATE dataTable
SET ACCX = (select b.cnt
from dataTable a
join
(SELECT Account,
COUNT(1) cnt
FROM dataTable
GROUP BY Account) b
on a.Account=b.Account)
,ACCR = 15481
,ACCF = 3
WHERE ID = 1625
I only have the access & can change to the bold part since rest of the query is generated by the tool I cannot change it & I have to update ACCX column with the count of the value in column Account. Is it possible to do?
Note: - Account column is already populated with values.
You cannot follow that query because it is for sqlserver and NOT oracle. It is simpler in oracle and does not need a join to itself.
This update will set the count for id 1625 only based on number of account numbers in datatable. See demo here;
http://sqlfiddle.com/#!4/d154c/1
update dataTable a
set ACCR = 15481,
ACCF = 3,
a.ACCX = (
select COUNT(*)
from dataTable b
where b.Account=a.Account)
WHERE a.ID = 1625;

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);

Oracle: Invalid identifier

I am using the following query in oracle. However, it gives an error saying that "c.par" in line 5 is an invalid parameter. No idea why. The columns exist. I checked. I have been struggling with this for a long time. All I want to do is to merge one table into another and update it using oracle. Could someone please help?
MERGE INTO SPRENTHIERARCHIES
USING ( SELECT c.PARENTCATEGORYID AS par,
e.rootcategoryId AS root
FROM SPRENTCATEGORIES c,SPRENTHIERARCHIES e
WHERE e.root (+)= c.par
) SPRENTCATEGORIES
ON (SPRENTHIERARCHIES.rootcategoryId = SPRENTCATEGORIES.parentcategoryId)
WHEN MATCHED THEN
UPDATE SET e.root=c.par
The e and c aliases only exist within the query in the using clause. You're trying to refer to them in the update clause. You're also using a column alias from the using clause against the target table, which doesn't have that column (unless your tables have both rootcategoryId and root, and parentCategoryId and par).
So this:
UPDATE SET e.root=c.par
should be:
UPDATE SET SPRENTHIERARCHIES.rootcategoryId= SPRENTCATEGORIES.par
And in that using clause you're trying to use column aliases as the same level of query, so this:
WHERE e.root (+)= c.par
should be:
WHERE e.rootcategoryId (+)= c.PARENTCATEGORYID
Your on clause is wrong too, as that is not using the column alias:
ON (SPRENTHIERARCHIES.rootcategoryId = SPRENTCATEGORIES.par)
But I'd suggest you replace the old syntax in the using clause with proper join clauses:
MERGE INTO SPRENTHIERARCHIES
USING ( SELECT c.PARENTCATEGORYID AS par,
e.rootcategoryId AS root
FROM SPRENTCATEGORIES c
LEFT JOIN SPRENTHIERARCHIES e
ON e.rootcategoryId = c.PARENTCATEGORYID
) SPRENTCATEGORIES
ON (SPRENTHIERARCHIES.rootcategoryId = SPRENTCATEGORIES.par)
WHEN MATCHED THEN
UPDATE SET SPRENTHIERARCHIES.rootcategoryId= SPRENTCATEGORIES.par
You have a more fundamental problem though, as you're trying to update a joining column; this will get:
ORA-38104: Columns referenced in the ON Clause cannot be updated
As Gordon Linoff suggested you can use an update rather than a merge. Something like:
UPDATE SPRENTHIERARCHIES h
SET h.rootcategoryId = (
SELECT c.PARENTCATEGORYID
FROM SPRENTCATEGORIES c
WHERE c.PARENTCATEGORYID = h.rootCategoryID
)
WHERE EXISTS (
SELECT null
FROM SPRENTCATEGORIES c
WHERE c.PARENTCATEGORYID = h.rootCategoryID
)
The where exists clause is there in case there not be a matching record - which the outer join in your original query implies. But in this form it's even more obvious that you're going to update rootcategoryId to the same value, since you're selecting the parentCategoryID which is equal to it. So the update (or merge) seems to be pointless.

Resources