Convert SQL Server update set from (with a join) to Oracle - oracle

SQL Server table definition:
CREATE TABLE #tmp_pricing
(
contract_nbr integer not null,
pricing_order integer not null,
priced_from_contract_flag char(1) COLLATE DATABASE_DEFAULT not null,
release_nbr integer null,
quantity decimal(25, 6) not null,
price decimal(25, 6) null,
price_future decimal(25, 6) null,
price_basis decimal(25, 6) null,
sum_qty decimal(25, 6) null,
row_nbr integer null,
new_quantity decimal(25, 6) null
)
Oracle global temporary table definition
CREATE GLOBAL TEMPORARY TABLE gtt_cpr_tmp_pricing
(
contract_nbr number not null,
pricing_order number not null,
priced_from_contract_flag char(1) not null,
release_nbr number not null,
quantity number(25,6) not null,
price number(18,6) null,
price_future number(18,6) null,
price_basis number(18,6) null,
sum_qty number(25,6) null,
row_nbr number null,
new_quantity number(25,6) null
)
ON COMMIT DELETE ROWS
tablespace temp
/
SQL Server SQL statement:
UPDATE #tmp_pricing
SET row_nbr = Q.row_nbr
FROM #tmp_pricing
JOIN (SELECT contract_nbr,
pricing_order,
priced_from_contract_flag,
release_nbr,
ROW_NUMBER() OVER (PARTITION BY contract_nbr
ORDER BY contract_nbr,
priced_from_contract_flag,
CASE WHEN pricing_order < 0
THEN 1
ELSE 0
END,
pricing_order
) AS row_nbr
FROM #tmp_pricing
) Q
ON #tmp_pricing.contract_nbr = Q.contract_nbr
AND #tmp_pricing.pricing_order = Q.pricing_order
AND #tmp_pricing.priced_from_contract_flag = Q.priced_from_contract_flag
AND COALESCE(#tmp_pricing.release_nbr, 0) = COALESCE(Q.release_nbr, 0)
Oracle statement (using Merge)
MERGE INTO gtt_cpr_tmp_pricing t
USING (SELECT contract_nbr,
pricing_order,
priced_from_contract_flag,
release_nbr,
ROW_NUMBER() OVER (PARTITION BY contract_nbr
ORDER BY contract_nbr,
priced_from_contract_flag,
CASE WHEN pricing_order < 0
THEN 1
ELSE 0
END,
pricing_order
) AS row_nbr
FROM gtt_cpr_tmp_pricing
)
Q
ON (t.contract_nbr = Q.contract_nbr AND
t.pricing_order = Q.pricing_order AND
t.priced_from_contract_flag = Q.priced_from_contract_flag AND
COALESCE(t.release_nbr, 0) = COALESCE(Q.release_nbr, 0)
)
WHEN MATCHED
THEN UPDATE SET row_nbr = Q.row_nbr;
This is the error I get:
ORA-30926: unable to get a stable set of rows in the source tables
I would like to convert the statement to an update .. set ..where exists
structure but am doing something wrong; here is my (first) attempt:
UPDATE gtt_cpr_tmp_pricing
SET row_nbr = (SELECT contract_nbr,
pricing_order,
priced_from_contract_flag,
release_nbr,
ROW_NUMBER() OVER (PARTITION BY contract_nbr
ORDER BY contract_nbr,
priced_from_contract_flag,
CASE WHEN pricing_order < 0
THEN 1
ELSE 0
END,
pricing_order
) AS row_nbr
FROM gtt_cpr_tmp_pricing ctp
WHERE gtt_cpr_tmp_pricing.contract_nbr = contract_nbr
AND gtt_cpr_tmp_pricing.pricing_order = pricing_order
AND gtt_cpr_tmp_pricing.priced_from_contract_flag = priced_from_contract_flag
AND COALESCE(gtt_cpr_tmp_pricing.release_nbr, 0) = COALESCE(release_nbr, 0)
)
WHERE EXISTS (SELECT 1
FROM gtt_cpr_tmp_pricing
WHERE gtt_cpr_tmp_pricing.contract_nbr = contract_nbr
AND gtt_cpr_tmp_pricing.pricing_order = pricing_order
AND gtt_cpr_tmp_pricing.priced_from_contract_flag = priced_from_contract_flag
AND COALESCE(gtt_cpr_tmp_pricing.release_nbr, 0) = COALESCE(release_nbr, 0)
);
Any assistance would be appreciated.
Murray

When you use inline views in Oracle, columns without an alias reference the tables in the inline view. You need to use a table alias to reference the table in the update clause inside your inline views. Note the price_outer alias and where it's used.
UPDATE gtt_cpr_tmp_pricing price_outer
SET row_nbr = (SELECT contract_nbr,
pricing_order,
priced_from_contract_flag,
release_nbr,
ROW_NUMBER() OVER (PARTITION BY contract_nbr
ORDER BY contract_nbr,
priced_from_contract_flag,
CASE WHEN pricing_order < 0
THEN 1
ELSE 0
END,
pricing_order
) AS row_nbr
FROM gtt_cpr_tmp_pricing ctp
WHERE price_outer.contract_nbr = contract_nbr
AND price_outer.pricing_order = pricing_order
AND price_outer.priced_from_contract_flag = priced_from_contract_flag
AND COALESCE(price_outer.release_nbr, 0) = COALESCE(release_nbr, 0)
)
WHERE EXISTS (SELECT 1
FROM gtt_cpr_tmp_pricing
WHERE price_outer.contract_nbr = contract_nbr
AND price_outer.pricing_order = pricing_order
AND price_outer.priced_from_contract_flag = priced_from_contract_flag
AND COALESCE(price_outer.release_nbr, 0) = COALESCE(release_nbr, 0)
);

Related

Managing dates when they're overlapping

How to write a procedure so that when adding a new insert, rows are added appropriately?
Let's say i have a table:
create table test_table
(
code varchar2(10) not null,
type varchar2(50) not null,
start_date date not null,
end_date date not null,
parameter number
);
1. First test case:
In table we have:
insert into test_table values ('CODE', 'a', to_date('01.01.2021', 'DD,MM,YYYY'), to_date('10.01.2021', 'DD,MM,YYYY'), 1);
[2021-01-01 - 2021-01-10] type = "a" parameter = 1
and when we want to insert:
insert into test_table values ('CODE', 'a', to_date('01.01.2021', 'DD,MM,YYYY'), to_date('20.01.2021', 'DD,MM,YYYY'), 1)
[2021-01-11 - 2021-01-20] type = "a" parameter = 1
*Result should be:
2021-01-01 - 2021-01-20 type = "a" parameter = 1*
2. Second test case:
In table we have:
insert into test_table values ('CODE', 'a', to_date('01.01.2021', 'DD,MM,YYYY'), to_date('10.01.2021', 'DD,MM,YYYY'), 1)
[2021-01-01 - 2021-01-10] type = "a" parameter = 1
and when we want to insert:
insert into test_table values ('CODE', 'a', to_date('06.01.2021', 'DD,MM,YYYY'), to_date('20.01.2021', 'DD,MM,YYYY'), 2)
[2021-01-06 - 2021-01-20] type = "a" parameter = 2
*in result we should have:
[2021-01-01 - 2021-01-05] type = "a" parameter = 1
[2021-01-06 - 2021-01-20] type = "a" parameter = 2*
3. Third test case:
In table we have:
insert into test_table values ('CODE', 'a', to_date('01.01.2021', 'DD,MM,YYYY'), to_date('10.01.2021', 'DD,MM,YYYY'), 1)
[2021-01-01 - 2021-01-20] type = "a" parameter = 1
and when we want to insert:
insert into test_table values ('CODE', 'a', to_date('06.01.2021', 'DD,MM,YYYY'), to_date('15.01.2021', 'DD,MM,YYYY'), 2)
[2021-01-06 - 2021-01-15] type = "a" parameter = 2
*in result we should have:
[2021-01-01 - 2021-01-05] type = "a" parameter = 1
[2021-01-06 - 2021-01-15] type = "a" parameter = 2
[2021-01-16 - 2021-01-20] type = "a" parameter = 1*
When you insert a new date range that is completely contained in the middle of an existing date range then you need to: INSERT of the new range; UPDATE the existing range to the portion of that range before the new range; and INSERT a new range for the portion of the existing range after the new range. So you need a total of 3 changes.
Similarly, when you insert a new date range that completely contains an existing range then you need to: INSERT the new range; and DELETE the existing range (or do a single UPDATE statement).
You can use a single MERGE statement for all of these actions:
MERGE INTO test_table dst
USING (
WITH new_data (code, type, start_date, end_date, parameter) AS (
SELECT 'CODE2', 'a', DATE '2021-01-01', DATE '2021-01-20', 2 FROM DUAL
)
SELECT NULL AS rid,
n.*,
0 AS status -- Insert
FROM new_data n
UNION ALL
-- Existing rows overlapping before
SELECT t.ROWID,
t.code,
t.type,
t.start_date,
n.start_date - INTERVAL '1' DAY,
t.parameter,
1 -- Update overlap before
FROM test_table t
INNER JOIN new_data n
ON ( t.start_date <= n.start_date
AND t.end_date >= n.start_date)
UNION ALL
SELECT t.ROWID,
t.code,
t.type,
n.end_date + INTERVAL '1' DAY,
t.end_date,
t.parameter,
CASE
WHEN n.start_date <= t.end_date AND t.end_date <= n.end_date
THEN 2 -- Delete
WHEN t.start_date < n.start_date AND n.end_date < t.end_date
THEN 0 -- Insert overlap afterwards
ELSE 1 -- Update overlap afterwards
END
FROM test_table t
INNER JOIN new_data n
ON ( t.start_date <= n.end_date
AND t.end_date >= n.start_date)
WHERE NOT (t.start_date <= n.start_date AND t.end_date <= n.end_date)
) src
ON (src.rid = dst.ROWID AND status > 0)
WHEN MATCHED THEN
UPDATE
SET code = src.code,
start_date = src.start_date,
end_date = src.end_date
DELETE
WHERE status = 2
OR src.start_date > src.end_date
WHEN NOT MATCHED THEN
INSERT (code, type, start_date, end_date, parameter)
VALUES (src.code, src.type, src.start_date, src.end_date, src.parameter);
db<>fiddle here

pl/sql statement Trigger

Suppose we have the following two tables:
OrderHeader(OrderID, Odate, CustID, Total)
Order_Item(OrderID,ItemID, Qty, Subtotal)
Write a statement-level trigger that updates the Total in the orderHeader table with
the total value of the order_item records whenever an insert, update or delete event
occurs on the order_item table. For any update error, raise an exception.
So far I have written this:
create or replace TRIGGER SECURE_ORDER
AFTER INSERT OR DELETE OR UPDATE ON Order_Item
declare
sum1 Number;
BEGIN
SELECT SUM(Qty*Price) Into sum1 FROM Order_Item Inner join Item Using(ItemID) WHERE OrderID=:OLD.OrderID;
UPDATE OrderHeader set Total=sum1 where OrderID=:OLD.OrderID;
EXCEPTION
WHEN too_many_rows then
dbms_output.put_line('Too many rows');
WHEN no_data_found then
dbms_output.put_line('No Data Found');
WHEN others then
dbms_output.put_line('other error');
END;
It is not correct however as the question statement says I have to write a statement level trigger which doesn't give me acces to NEW and OLD keyword. I don't know how to go about doing this in a statement level trigger. Any help would be appreciated.
You can just use a single MERGE statement:
CREATE TRIGGER SECURE_ORDER
AFTER INSERT OR DELETE OR UPDATE ON Order_Item
BEGIN
MERGE INTO OrderHeader dst
USING (
SELECT COALESCE( oh.OrderID, o.OrderID ) AS OrderID,
COALESCE( SUM( o.Qty*i.Price ), 0 ) AS total
FROM OrderHeader oh
FULL OUTER JOIN Order_Item o
ON oh.OrderID = o.OrderID
LEFT OUTER JOIN Item i
ON ( o.ItemID = i.ItemID )
GROUP BY COALESCE( oh.OrderID, o.OrderID )
) src
ON ( src.OrderID = dst.OrderID )
WHEN MATCHED THEN
UPDATE SET total = src.total
WHEN NOT MATCHED THEN
INSERT ( OrderID, Total ) VALUES ( src.OrderID, src.Total );
END;
/
Then, if you have the tables:
CREATE TABLE Item (
ItemID NUMBER(10,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT Item__ItemID__PK PRIMARY KEY,
Name VARCHAR2(20)
NOT NULL,
Price NUMBER(12,2)
NOT NULL
CONSTRAINT Item__Price_GTE_0__CHK CHECK ( Price >= 0 )
);
CREATE TABLE Order_Item (
OrderID INT
-- CONSTRAINT Order_Item__OrderID__FK REFERENCES Orders ( OrderID )
NOT NULL,
Qty INT
NOT NULL
CONSTRAINT Order_Item__Qty_GT_0__CHK CHECK ( Qty > 0 ),
ItemID INT
NOT NULL
CONSTRAINT Order_Item__ItemID__FK REFERENCES Item ( ItemId ),
CONSTRAINT Order_Item__OID__IID__PK PRIMARY KEY ( OrderID, ItemID )
);
CREATE TABLE OrderHeader (
OrderID INT
NOT NULL,
total NUMBER(14,2)
NOT NULL
);
Then if you:
INSERT INTO Item ( Name, Price ) VALUES ( 'Item 001', 1.00 );
INSERT INTO Item ( Name, Price ) VALUES ( 'Item 002', 2.00 );
INSERT INTO Item ( Name, Price ) VALUES ( 'Item 003', 2.50 );
INSERT INTO Order_Item ( OrderID, ItemID, Qty ) VALUES ( 1, 1, 3 );
INSERT INTO Order_Item ( OrderID, ItemID, Qty ) VALUES ( 1, 2, 1 );
INSERT INTO Order_Item ( OrderID, ItemID, Qty ) VALUES ( 1, 3, 2 );
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 10
Then if you do:
UPDATE Order_Item
SET Qty = 8
WHERE OrderID = 1
AND ItemID = 1;
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 15
Then if you do:
DELETE FROM Order_Item
WHERE ( OrderID, ItemID ) IN ( (1,2), (1,3) );
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 8
And, finally, if you do:
DELETE FROM Order_Item;
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 0
db<>fiddle here

Can't understand Oracle logic for Procedure

PROCEDURE DELETE_X1
IS
v_emp_unum VARCHAR2 (25);
BEGIN
/*FOR rec
IN (SELECT l2.*,
row_number ()
OVER (PARTITION BY case_uid,nvl(min_eff_date, eff_begin_date)
ORDER BY case_uid,nvl(min_eff_date, eff_begin_date))
rw,
ROWID rid
FROM (SELECT l1.*,
MIN (
CASE
WHEN eff_end_date = next_begin - 1
THEN
eff_begin_date
END)
OVER (PARTITION BY case_uid)
min_eff_date,
MAX (
CASE
WHEN previous_end <>
TO_DATE ('12/31/9999',
'mm/dd/yyyy')
AND previous_end + 1 = eff_begin_date
THEN
eff_end_date
END)
OVER (PARTITION BY case_uid)
max_end_date
FROM (SELECT GT.*,
LEAD (
EFF_begin_DATE)
OVER (PARTITION BY CASE_UID
ORDER BY EFF_BEGIN_DATE)
next_begin,
LAG (
EFF_end_DATE)
OVER (PARTITION BY CASE_UID
ORDER BY EFF_BEGIN_DATE)
previous_end
FROM TABLE_OUTPUT GT
WHERE SRC = 'ERICSSON' AND STATUS_CODE = 'X1') l1)
l2)*/
FOR rec
IN (SELECT l2.*,
ROW_NUMBER ()
OVER (PARTITION BY case_uid, min_eff_date, max_end_date
ORDER BY case_uid, min_eff_date, max_end_date)
rw,
ROWID rid
FROM (SELECT l1.*,
(MAX (
start_at)
OVER (
PARTITION BY case_uid
ORDER BY EFF_BEGIN_DATE
ROWS UNBOUNDED PRECEDING))
min_eff_date,
(MIN (
break_at)
OVER (
PARTITION BY case_uid
ORDER BY EFF_BEGIN_DATE
ROWS BETWEEN CURRENT ROW
AND UNBOUNDED FOLLOWING))
max_end_date
FROM (SELECT GT.*,
(CASE
WHEN LAG (
EFF_end_DATE)
OVER (
PARTITION BY CASE_UID
ORDER BY EFF_BEGIN_DATE) =
EFF_BEGIN_DATE
- 1
THEN
NULL
ELSE
EFF_BEGIN_DATE
END)
start_at,
(CASE
WHEN LEAD (
EFF_BEGIN_DATE)
OVER (
PARTITION BY case_uid
ORDER BY EFF_BEGIN_DATE) =
CASE
WHEN EFF_end_DATE <>
TO_DATE (
'12/31/9999',
'mm/dd/yyyy')
THEN
EFF_end_DATE
+ 1
ELSE
EFF_end_DATE
END
THEN
NULL
ELSE
EFF_end_DATE
END)
break_at
FROM TABLE_OUTPUT GT
WHERE SRC = 'ERICSSON' AND STATUS_CODE = 'X1') l1)
l2)
The part of code is commented out an re written.
commented out code output
OFF TIME 1/1/2017 1/7/2017 X1
OFF TIME 1/8/2017 2/1/2017 X1
New code output
OFF TIME 1/1/2017 2/1/2017 X1
NORMAL 2/2/2017 2/2/2017 AB
OFF TIME 2/20/2017
The LAG function is used to access data from a previous row.
The LEAD function is used to return data from rows further down the result set.
aggregate functions MIN , MAX
i am pretty confused with the flow of code.
I can't understand that code on the whole please explain the logic for the code

Oracle update subquery of records

I need to update a list of record given by a subquery, sniffing around on the web I tried this structure:
UPDATE
(
SELECT
a.COL1
FROM
TABLE1 a,
TABLE2 b
WHERE
a.field1 = b.field1
) update_tbl
SET
update_tbl.COL1 = 'VALUE'
But it returns to me this Oracle error:
-> ORA-01779: Cannot modify a column which maps to a non key-preserved table
My query is the following one:
UPDATE
(
SELECT
imp.*
FROM table1 imp
JOIN table2 sp ON imp.id_p = sp.id_p
JOIN table3 cs ON sp.id_s = cs.id_s
JOIN table4 cb ON cb.id_c = cs.id_c
WHERE
imp.id_b = cb.id_b
AND (
(to_char(imp.p,'yyyymm') < to_char(cb.data_in,'yyyymm')) OR
(cb.data_fi IS NOT NULL AND to_char(imp.p,'yyyymm') > to_char(cb.data_fi,'yyyymm'))
)
and (
(imp.v is not null) or
(imp.v_s is not null and imp.v_s <> 0) or
(imp.imp_co is not null and imp.imp_co <> 0) or
(imp.imp_acc is not null and imp.imp_acc <> 0)
)
) i
SET
i.v = null,
i.v_s = 0,
i.imp_co = 0,
i.imp_acc = 0,
i.ID_S_CONT = 'N',
i.ID_T_COMP = 'P',
i.date_upd = SYSDATE,
i.user_upd = 'SeR'
The subquery return 82 rows (tested now), and I do want to modify only that rows, What am I doing wrong?
I think you are updating to imp table. so you can try MERGE like below
MERGE INTO
IMP A
USING
( SELECT
imp.*
FROM table1 imp
JOIN table2 sp ON imp.id_p = sp.id_p
JOIN table3 cs ON sp.id_s = cs.id_s
JOIN table4 cb ON cb.id_c = cs.id_c
WHERE
imp.id_b = cb.id_b
AND (
(to_char(imp.p,'yyyymm') < to_char(cb.data_in,'yyyymm')) OR
(cb.data_fi IS NOT NULL AND to_char(imp.p,'yyyymm') >
to_char(cb.data_fi,'yyyymm'))
)
and (
(imp.v is not null) or
(imp.v_s is not null and imp.v_s <> 0) or
(imp.imp_co is not null and imp.imp_co <> 0) or
(imp.imp_acc is not null and imp.imp_acc <> 0)
)) B
ON (A.ID =B.ID)
WHEN MATCHED THEN
UPDATE SET
A.v = null,
A.v_s = 0,
A.imp_co = 0,
A.imp_acc = 0,
A.ID_S_CONT = 'N',
A.ID_T_COMP = 'P',
A.date_upd = SYSDATE,
A.user_upd = 'SeR'
Will update the table given by subquery. here A.ID =B.ID use the primary key.

Oracle update Query does not work

I have an ORACLE query which does not work for this query.
UPDATE emp a set a.Enq_Status = 'PROGRESS'
where exists (select * from emp a, Data b
WHERE a.SA_Enq_Status is NULL
and a.EQ_Status = 'ACTIVATED'
and a.Activation_Return_Code = 0
and a.Alert_Code > 0
and a.User_Id = b.User_Id
and (b.Is_Associate is NULL or b.Is_Associate = 0)
and (b.Stk_Schd is NULL)
and (b.Stk_Dis_Amt is NULL)
);
My Problem is I want to update mutiple records in the table emp (A) to status to 'PROGRESS'.But when executing this query all records in a.Enq_status is changes.Please help me in this.Updation is not correct.Please help me in this
You have specified table emp both in the update query and the subquery too, Oracle will treat these as seperate tables.
You need a correlated subquery:
UPDATE emp a
set a.Enq_Status = 'PROGRESS'
where exists (select 'X'
from Data b
WHERE a.SA_Enq_Status is NULL
and a.EQ_Status = 'ACTIVATED'
and a.Activation_Return_Code = 0
and a.Alert_Code > 0
and a.User_Id = b.User_Id
and (b.Is_Associate is NULL or b.Is_Associate = 0)
and (b.Stk_Schd is NULL)
and (b.Stk_Dis_Amt is NULL));
You could probably just update the emp table without the need for the subquery though if you link to the Data table in the where clause...
Your update statement will update all the records in emp table because you don't specify the records to update. If your sub-query returns at least one row, the all the emp records will be updated. If it returns no rows, then none of the records will be updated.
Change your update like this:
UPDATE emp SET Enq_Status = 'PROGRESS'
WHERE id in
(SELECT a.id
FROM emp a, Data b
WHERE a.SA_Enq_Status is NULL and a.EQ_Status = 'ACTIVATED'
and a.Activation_Return_Code = 0 and a.Alert_Code > 0
and a.User_Id = b.User_Id and (b.Is_Associate is NULL or b.Is_Associate = 0)
and (b.Stk_Schd is NULL)and (b.Stk_Dis_Amt is NULL)
);
Try:
UPDATE emp a
SET a.enq_status = 'PROGRESS'
WHERE a.sa_enq_status IS NULL
AND a.eq_status = 'ACTIVATED'
AND a.activation_return_code = 0
AND a.alert_code > 0
AND EXISTS
(SELECT 'X'
FROM data b
WHERE a.user_id = b.user_id
AND ( b.is_associate IS NULL OR b.is_associate = 0 )
AND b.stk_schd IS NULL
AND b.stk_dis_amt IS NULL);

Resources