Multiple Join tables in classic report; Inserting only first row instead of checked row - oracle

I have question on classic report, which is based on multiple table joins. On which I have written process to loop and insert only checked box item. but it only select first item. However if do this on a single table it work properly. I would appreciate if someone can help me on inserted selected record when query is based on multiple joins.
My query is as below.
select apex_item.checkbox2(1, ord.rowid) sel,
apex_item.text(2,cust.name) Customer,
apex_item.text(3, it.item_id) Item,
apex_item.text(4,it.product_id) Product,
apex_item.text(5,price) price,
apex_item.text(6,quantity)||apex_item.hidden(7,ord.id) qty
from s_ord ord,
s_item it,
s_customer cust
where ord.id=it.ord_id
and cust.id=ord.customer_id
My process is as follow;
for i in 1..apex_application.g_f01.count loop
APEX_DEBUG_MESSAGE.LOG_MESSAGE(p_message => 'G_F01 : '||APEX_APPLICATION.G_F01(i), p_level => 1);
APEX_DEBUG_MESSAGE.LOG_MESSAGE(p_message => ' Q1 : '||APEX_APPLICATION.G_F02(i), p_level => 1);
APEX_DEBUG_MESSAGE.LOG_MESSAGE(p_message => ' P1 : '||APEX_APPLICATION.G_F03(i), p_level => 1);
end loop;
end;

As far as I can tell, you can't do that if a tabular form is created as a JOIN of two (or more) tables (didn't investigate why).
Here's what I do:
I base my tabular form on one table (the one I'm planning to work (insert, update) with
columns, that are normally fetched from other tables (using JOINs) are displayed using functions
That fixes the issue.
For example: you want to update employee's information, but also display department name they work in.
Don't:
select e.empno,
e.ename,
d.dname,
e.sal
from emp e join dept d on e.deptno = d.deptno;
Do:
create function f_dname (par_deptno in dept.deptno%type)
return dept.dname%type
is
retval dept.dname%type;
begin
select max(d.dname)
into retval
from dept d
where d.deptno = par_deptno;
return retval;
end;
/
select e.empno,
e.ename,
f_dname (e.deptno) dname, --> function instead of DEPT.DNAME
e.sal
from emp e; --> no join
I hope it'll help.

Related

How to create trigger based on two joined tables and throw error if rows missing from third table?

I have tables A, B and C.
A and B can be joined using unique ID
and C can be joined to B using another unique ID2.
Like
A.ID = B.ID
and B.ID = C.ID2
Now, I would like to have a trigger to check if there is record or not in C table during insert/update process in table A and throw error if not.
I´m using Oracle 12c, so more advanced options are also welcome.
You will need to work the details, here is the general outline of what such a trigger would look like:
CREATE OR REPLACE TRIGGER tablea_trg1
AFTER INSERT OR UPDATE
ON tablea
DECLARE
l_cnt INTEGER;
BEGIN
SELECT COUNT (*) c
INTO l_cnt
FROM tablea
INNER JOIN tableb ON tablea.abid = tableb.abid
INNER JOIN tablec ON tableb.cid = tablec.cid
WHERE tablea.abid = :new.abid;
IF l_cnt = 0
THEN
raise_application_error (-2001, 'Boo boo happened');
END IF;
END;

Where does the table mutation take place here (Oracle)?

I'm dealing with oracle SQL now, and trying to create a trigger. For some reasonn it shows me the mutated table error, however, I don't see where it gets mutated at all (it's just a select without even counts, however, it has joins). Where should I at least start in writing a compound trigger for the solution? There seems to be just a few examples and they are too far from mine to understand how it should work at all in this particular situation.
CREATE OR REPLACE TRIGGER "EVGENIJ_BOBROVICH"."FIX_UPD_LIMITS"
BEFORE UPDATE OR DELETE ON "EVGENIJ_BOBROVICH"."MAP_CALCULATION_SHOP_LIMITS"
FOR EACH ROW
DECLARE
is_deleted_dependant VARCHAR2(1 BYTE);
is_editable_dependant VARCHAR2(1 BYTE);
BEGIN
SELECT IS_DELETE, IS_EDITABLE INTO is_deleted_dependant, is_editable_dependant
FROM MAP_CALCULATION MC INNER JOIN map_calculation_group MG
ON MC.ID_CALC = MG.ID_CALC INNER JOIN map_calculation_shop_limits MS ON MG.ID_GROUP = ms.id_group
WHERE :OLD.ID_GROUP = MG.ID_GROUP AND MG.ID_CALC = MC.ID_CALC;
IF UPDATING AND (is_editable_dependant = 'F' OR is_deleted_dependant = 'T') THEN
RAISE_APPLICATION_ERROR( -20004, '....Error......' );
ELSIF DELETING AND (is_editable_dependant = 'F' OR is_deleted_dependant = 'T') THEN
RAISE_APPLICATION_ERROR( -20005, '...AnotherError...' );
END IF;
END;
/
The select itself without a trigger (without the into and old of course as well, I used a bind variable instead of old) works fine...
It happens here:
INNER JOIN map_calculation_shop_limits
You can't select from the same table you're updating as it is ... well, mutating.
For updating purposes, you'd
FROM map_calculation mc
INNER JOIN map_calculation_group mg ON mc.id_calc = mg.id_calc
-- INNER JOIN map_calculation_shop_limits MS ON MG.ID_GROUP = ms.id_group --> this
WHERE :old.id_group = mg.id_group
AND mg.id_calc = mc.id_calc
AND mg.id_group = :new.id_group; --> and this
For deleting, you'd use :old.id_group which means that you'll need to rewrite code you use into two parts: one that'll handle updates, and another to handle deletes.

Solving Procedure with no parameters

Hey guys just here to see if you guys can help me solve this Procedure problem I am running into. Long story short I made a new table called
Create Table ClientHistoricalPurchases(
ClientID varchar2(6) constraint clientidhistorical references Clients,
DistinctProducts number (9),
TotalProducts number(9),
TotalCost number (9,2),
Primary Key (ClientID));
And I want to populate/update this table by running a procedure that reads primarily from the following table:
create table OrderDetails(
OrderID varchar2(6) CONSTRAINT orddetpk PRIMARY KEY,
ProductID varchar2(6) CONSTRAINT prdfk REFERENCES Products ,
UnitPrice number(10,2),
Quantity number(4),
Discount number(3),
ShippingDate date);
I do a couple of joins with two more tables called Orders and Clients but those are trivial joins using the Primary Key's/FK.
So the goal of this procedure is that when I run it I want to loop through order details and I want to calculate the distinct amount of products bought by a Client, the total products and the total purchase amount and I want to update an existing record with the new values if its in the new ClientHistoricalPurchases table if not I want to add a new record for it. So this is what I wrote but its giving me errors:
Create or Replace Procedure Update_ClientHistPurch as
Cursor C1 is
Select orderid, orders.clientid, productid, unitprice, quantity, discount
from orderdetails
Inner join orders on orderdetails.orderid = orders.clientid
for update of TotalCost;
PurchaseRow c1%RowType;
DistinctProducts orderdetails.quantity%type;
TotalProducts orderdetails.quantity%type;
ProposedNewBalance orderdetails.unitprice%type;
Begin
Begin
Begin
Begin
Open C1;
Fetch c1 into PurchaseRow;
While c1% Found Loop
Select count(distinct productid)
into DistinctProducts
from orderdetails
Inner join orders on orderdetails.orderid = orders.orderid
Inner join clients on orders.clientid = clients.clientid
where clients.clientid = purchaserow.clientid;
end;
Select count(ProductID)
into TotalProducts
from orderdetails
Inner join orders on orderdetails.orderid = orders.orderid
Inner join clients on orders.clientid = clients.clientid
where clients.clientid = purchaserow.clientid;
end;
Select sum((unitprice * quantity) - discount)
into ProposedNewBalance
from orderdetails
Inner join orders on orderdetails.orderid = orders.orderid
Inner join clients on orders.clientid = clients.clientid
where clients.clientid = purchaserow.clientid;
end;
If purchaserow.clientid not in ClientHistoricalpurchases.clientid then
insert into ClientHistoricalPurchases values (purchaserow.clientid,DistinctProducts, TotalProducts, ProposedNewBalance);
End if;
If purchaserow.clientid in ClientHistoricalPurchases.clientid then
Update Clienthistoricalpurchases
set clienthistoricalpurchases.distinctproducts = distinctproducts, clienthistoricalpurchases.totalproducts = totalproducts, clienthistoricalpurchases.totalcost = ProposedNewBalance
where purchaserow.clientid = clienthistoricalpurchases.clientid;
end if;
end loop;
end;
Errors are the following:
Error(27,4): PLS-00103: Encountered the symbol ";" when expecting one
of the following: loop The symbol "loop" was substituted for ";"
to continue.
Error(33,7): PLS-00103: Encountered the symbol "JOIN"
when expecting one of the following: , ; for group having
intersect minus order start union where connect
Any help is appreciated guys. Thanks!
In addition to the comments and answers you've already been given, I believe you have massively overcomplicated your procedure. You're doing things very procedurally, rather than thinking in sets as you should be. You are also getting the aggregated columns in three queries that are essentially identical (e.g. same tables, join conditions and predicates) - you could combine them all to get the three results in a single query.
It looks like you're trying to insert into the clienthistoricalpurchases table if a row doesn't already exist for that client, otherwise you update the row. That immediately screams "MERGE statement" to me.
Combining all that, I think your current procedure should contain just a single merge statement:
MERGE INTO clienthistoricalpurchases tgt
USING (SELECT clients.client_id,
COUNT(DISTINCT od.productid) distinct_products,
COUNT(od.productid) total_products,
SUM((od.unitprice * od.quantity) - od.discount) proposed_new_balance
FROM orderdetails od
INNER JOIN orders
ON orderdetails.orderid = orders.orderid
INNER JOIN clients
ON orders.clientid = clients.clientid
GROUP BY clients.client_id) src
ON (tgt.clientid = src.client_id)
WHEN NOT MATCHED THEN
INSERT (tgt.clientid,
tgt.distinctproducts,
tgt.totalproducts,
tgt.totalcost)
VALUES (src.clientid,
src.distinct_products,
src.total_products,
src.proposed_new_balance)
WHEN MATCHED THEN
UPDATE SET tgt.distinctproducts = src.distinct_products,
tgt.totalproducts = src.total_products,
tgt.totalcost = src.proposed_new_balance;
However, I have some concerns over your current logic and/or data model.
It seems like you're expecting at most one row per clientid to appear in clienthistoricalpurchases. What if a clientid has two or more different orders? Currently you would overwrite any existing row.
Also, do you really want to apply this logic across all orders every single time it gets run?
Line 28 of your code, the first END that follows WHILE, should be END LOOP

Oracle cursor with variables help needed

I am trying to do a cursor which does something like below, struggling with different approaches with no results. Seems, I won't be able to do it by myself, and decided to ask you for help.
Below code shows what I want to achieve rather than ready approach. Please help.
I dont know it it matters but note, that I need to update CUSTOMERS in loop. I also need to select some data from another table referencing customer in this loop, then insert something to third table and update customer table.
DECLARE
CURSOR MY_CURSOR
IS
SELECT CUSTOMERID FROM CUSTOMERS WHERE ACTIVE = 1 ;
MY_RECORD MY_CURSOR%ROWTYPE;
BEGIN
FOR MY_RECORD IN MY_CURSOR
LOOP
DECLARE TEMPORARY_TABLE TABLE (A DATE, B NUMBER, C VARCHAR)
INSERT INTO #TEMPORARY_TABLE(A,B,C) (SELECT CREATEDDATE, ID, NAME FROM ACCOUNT WHERE CUSTOMER = MY_RECORD.CUSTOMERID)
INSERT INTO SOME_EVENT_TABLE(ID, NAME, DATE, ACCOUNT_ID) VALUE (some_seq.NEXTVAL, #TEMPORARY_TABLE[C], #TEMPORARY_TABLE[A], #TEMPORARY_TABLE[B])
UPDATE CUSTOMERS SET LAST_ACCOUNT_CHECK_NAME=#TEMPORARY_TABLE(C), LAST_INSERTED_EVENT_ID = some_seq.CURRVAL WHERE ID = MY_RECORD.CUSTOMERID
END LOOP;
COMMIT;
END;
First, you can't declare a temporary table in Oracle like you do in SQL Server. However, you really don't need it here anyway.
Something like this should work:
FOR MY_RECORD IN MY_CURSOR LOOP
FOR R IN (SELECT CREATEDDATE, ID, NAME
FROM ACCOUNT WHERE CUSTOMER = MY_RECORD.CUSTOMERID) LOOP
INSERT INTO some_event_table(ID, NAME, DATE, ACCOUNT_ID)
VALUES (some_seq.NEXTVAL, R.NAME, R.CREATEDATE, R.ID);
UPDATE customers
SET last_account_check_name = R.name
, last_inserted_event_id = some_seq.CURRVAL
WHERE id = MY_RECORD.CUSTOMER_ID;
END LOOP;
END LOOP;
COMMIT;
Row by row actions in SQL are terribly inefficient. You will get vastly better performance if you do this in a set-based way.
INSERT INTO some_event_table(ID, NAME, DATE, ACCOUNT_ID)
SELECT some_seq.NEXTVAL, a.name, a.createdate, a.id
FROM ACCOUNT a
INNER JOIN customers c ON c.customerid = a.customerid
WHERE c.active = 1;
UPDATE customers
SET last_account_check_name =
( SELECT a.name FROM account a WHERE a.customerid = c.customerid ),
last_inserted_event_id = some_seq.CURRVAL
WHERE c.active = 1;
There may be concurrency issues with that (what happens if customers is updated between the two statements?), but that might be good enough for your needs.

MERGE output cursor of SP into table?

i have a Stored Procedure which returns output as a ref cursor. I would like to store the output in another table using the MERGE statement. I'm having problems however mixing all the statements together (WITH, USING, MERGE etc..).
Can somebody assist? Thanks!
This is the table i want the output in (STOP_TIME is left out on purpose):
TABLE: **USER_ALLOCATION**
START_TIME date NOT NULL Primary Key
USER_ID number NOT NULL Primary Key
TASK_ID number NULL
This is the SP:
create or replace
PROCEDURE REPORT_PLAN_AV_USER
(
from_dt IN date,
to_dt IN date,
sysur_key IN number,
v_reservations OUT INTRANET_PKG.CURSOR_TYPE
)
IS
BEGIN
OPEN v_reservations FOR
with
MONTHS as (select FROM_DT + ((ROWNUM-1) / (24*2)) as DT from DUAL connect by ROWNUM <= ((TO_DT - FROM_DT) * 24*2) + 1),
TIMES as (select DT as START_TIME,(DT + 1/48) as STOP_TIME from MONTHS where TO_NUMBER(TO_CHAR(DT,'HH24')) between 8 and 15 and TO_NUMBER(TO_CHAR(DT,'D')) not in (1,7))
select
TIMES.START_TIME,
TIMES.STOP_TIME,
T.TASK_ID,
sysur_key USER_ID,
from
TIMES
left outer join (ALLOCATED_USER u INNER JOIN REQUIRED_RESOURCE r ON u.AU_ID = r.RR_ID INNER JOIN TASK t ON r.TASK_ID = t.TASK_ID)
ON u.USER_ID = sysur_key AND t.PLAN_TYPE = 3 AND TIMES.start_time >= TRUNC30(t.START_DATE) AND TIMES.start_time < TRUNC30(t.FINISH_DATE)
where u.USER_ID is null OR u.USER_ID = sysur_key
order by START_TIME ASC;
END;
I don't think you can resuse the cursor unless you write a lot of procedural code.
Can't you write a single merge statement and drop procedure REPORT_PLAN_AV_USER?
If you still need procedure REPORT_PLAN_AV_USE you can create a view that you use in procedure REPORT_PLAN_AV_USER and in your merge statement (to prevent code duplication).

Resources