Oracle Apex - Loop Items - oracle

I have a form where an user can add a record for a customer, issue is, each customer can have multiple products.
My insert (record) process basically checks if there's info added on the product items and loops for the necessary amount of times, e.g.:
User adds customer ABCDE and selects 2 products, 2 records for customer ABCDE will be inserted with the 2 new products.
Issue is that I can't get Apex to insert both products. I have something like this:
LOOP
V_COUNTER := V_COUNTER + 1;
EXIT WHEN V_COUNTER > V_AMOUNT_OF_PRODUCTS_COUNTER;
INSERT INTO MY_TABLE (CUSTOMER, PRODUCT)
VALUES (:P2_MY_TEXT_CUSTOMER_ITEM, :P2_PRODUCT_||V_COUNTER);
END LOOP;
If the user adds 3 products:
result: 3 rows of the same customer with the same (first item) product name
desired result: 3 rows of the same product with products being :P2_PRODUCT_1, :P2_PRODUCT_2 and :P2_PRODUCT_3.
Thanks

Bind variables cannot be declaratively named within a block of pl/sql code, but you can pass them as an argument with the function APEX_UTIL.GET_SESSION_STATE. LPAD transforms "1" to "01".
So your code would be (untested):
LOOP
V_COUNTER := V_COUNTER + 1;
EXIT WHEN V_COUNTER > V_AMOUNT_OF_PRODUCTS_COUNTER;
INSERT INTO MY_TABLE (
CUSTOMER,
PRODUCT)
VALUES (
:P2_MY_TEXT_CUSTOMER_ITEM
,apex_util.get_session_state (p_item => 'P2_PRODUCT'||'_'||LPAD(V_COUNTER,2,'0')));
END LOOP;
or with a for loop:
FOR r IN 1 .. V_AMOUNT_OF_PRODUCTS_COUNTER LOOP
INSERT INTO MY_TABLE (
CUSTOMER,
PRODUCT)
VALUES (
:P2_MY_TEXT_CUSTOMER_ITEM
,apex_util.get_session_state (p_item => 'P2_PRODUCT'||'_'||LPAD(r,2,'0')));
END LOOP;

Related

Oracle: Update Every Row in a Table based off an Array

So i'm trying to create some seed data for a database that uses zip codes. I've created an array of 22 arbitrary zip code strings, and i'm trying to loop through the array and update one of the zips to every row in a table. Based on what I read and tried (I'm a 1st year, so I'm probably missing something), this should work, and does when I just output the array value based on the count of the table. this issue is in the row id subquery. When I run it in my console, it doesn't throw any errors, but it never completes and I think it's stuck in an infinite loop. How can I adjust this so that it will update the field and not get stuck?
declare
t_count NUMBER;
TYPE zips IS VARRAY(22) OF CHAR(5);
set_of_zips zips;
i NUMBER;
j NUMBER :=1;
BEGIN
SELECT count(*) INTO t_count FROM T_DATA;
set_of_zips:= zips('72550', '71601', '85920', '85135', '95451', '90021', '99611', '99928', '35213', '60475', '80451', '80023', '59330', '62226', '27127', '28006', '66515', '27620', '66527', '15438', '32601', '00000');
FOR i IN 1 .. t_count LOOP
UPDATE T_DATA
SET T_ZIP=set_of_zips(j)
---
WHERE rowid IN (
SELECT ri FROM (
SELECT rowid AS ri
FROM T_DATA
ORDER BY T_ZIP
)
) = i;
---
j := j + 1;
IF j > 22
THEN
j := 1;
END IF;
END LOOP;
COMMIT;
end;
You don't need PL/SQL for this.
UPDATE t_data
SET t_zip = DECODE(MOD(ROWNUM,22)+1,
1,'72550',
2,'71601',
3,'85920',
4,'85135',
5,'95451',
6,'90021',
7,'99611',
8,'99928',
9,'35213',
10,'60475',
11,'80451',
12,'80023',
13,'59330',
14,'62226',
15,'27127',
16,'28006',
17,'66515',
18,'27620',
19,'66527',
20,'15438',
21,'32601',
22,'00000')

Sorting Data at the runtime in Oracle PL/SQL using Associative array

I am trying to sort a data from runtime query and no way I can do that without use of a collection. Essentially, I have 2 columns in Service table - SERVICE_ID, SERVICE_NAME. I have created an Associative array in a package so that I can use it in my procedure.
TYPE g_vc_arr IS TABLE OF VARCHAR2(4000) INDEX BY PLS_INTEGER;
In my pl/sql block I am creating a variable like below:
service_list my_pkg.g_vc_arr;
I am assigning the SERVICE_ID and SERVICE_NAME to this variable like below:
LOOP
service_list(services.SERVICE_ID) := services.SERVICE_NAME;
END LOOP;
Now, I am using this to sort the name column like below snippet. I am able to print the name but in this process, I am losing the ID.
for query_result_row in (SELECT * from table(service_list) order by 1) loop
dbms_output.put_line(query_result_row.COLUMN_VALUE);
end loop;
I need both ID and NAME to process further. How can I get both?
As others have mentioned, you don't need to sort it as it is already held in the order you defined it:
create or replace package demo
as
type currency_tt is table of varchar2(3) index by pls_integer;
currencies demo.currency_tt :=
demo.currency_tt
( 1 => 'USD'
, 2 => 'GBP'
, 3 => 'EUR' );
end demo;
begin
for r in (
select * from table(demo.currencies)
)
loop
dbms_output.put_line(r.column_value);
end loop;
end;
Output:
USD
GBP
EUR
But you don't need SQL, as you can iterate over an associative array procedurally:
declare
i pls_integer := demo.currencies.first;
begin
while i is not null loop
dbms_output.put_line(i||' '||demo.currencies(i));
i := demo.currencies.next(i);
end loop;
end;
Or from 21c you can loop more conveniently like this:
begin
for id, ccy in pairs of demo.currencies loop
dbms_output.put_line(id || ' '|| ccy);
end loop;
end;
1 USD
2 GBP
3 EUR
I changed my approach a bit to solve this issue. I created a package with 2 column TYPE as record and an associative array of the record type:
create or replace package my_pkg is
TYPE g_rec_type IS RECORD (ID NUMBER, name VARCHAR2(4000));
TYPE g_rec_arr IS TABLE OF g_rec_type INDEX BY PLS_INTEGER;
end my_pkg;
In my actual PL/sqL block I created couple of variables:
i NUMBER := 0;
service_list MY_PKG.g_rec_arr;
Then I created a loop to assign the values to array:
LOOP
service_list(i).ID := services.SERVICE_ID;
service_list(i).NAME := services.SUMMARY;
i := i+1;
END LOOP;
Further to sort the value I used following:
FOR query_result_row in (SELECT ID, NAME from table(service_list) order by NAME) LOOP
dbms_output.put_line('ID is =>' || query_result_row.ID|| ' Name is =>' || query_result_row.NAME);
END LOOP;
And it worked as expected.

How can I speed up the oracle query? ( where in( bla bla ))

I have 2000 numbers (Uniq Primary Key). I want to get contact information of numbers. My database is Oracle.
I use IN(bla,bla) in my query. It works slow because of this.
Example My Query:
SELECT p.*,t.*
FROM PERSONEL p
LEFT OUTER JOIN CODE_TITLE t ON t.Id = p.TitleId
WHERE ID IN(1,2,....,2000)
When the query runs, it takes about 10-12 seconds.
Is there a method to use instead of IN(bla, bla)? Can you explain with an example ?
Put your numbers (or whatever they really are) in a table. Let's call it LIST_TABLE. Then
SELECT p.*,t.*
FROM PERSONEL p
LEFT OUTER JOIN CODE_TITLE t ON t.Id = p.TitleId
WHERE ID IN(select list_id from list_table)
The type of table for LIST_TABLE (normal, GTT, external) will depend on where the values come from and your best mechanism for loading them.
You can use xmltable('1 to 2000') as a derived table in order to generate integer set starting form 1 upto 2000, incrementing by 1 :
SELECT p.*,t.*
FROM PERSONEL p
LEFT JOIN CODE_TITLE t ON t.Id = p.TitleId
WHERE Id IN ( SELECT TO_NUMBER(column_value) FROM xmltable('1 to 2000') )
P.S. indeed using WHERE ID BETWEEN 1 AND 2000 would suffice with index created on CODE_TITLE.ID in order to increase the query performance.
create or replace type myTcType as table of number(16,0);
create or replace function in_list(p_string in varchar2) return myTcType
as
l_data myTcType := myTcType();
l_string long default p_string || ',';
l_n number;
begin
loop
exit when l_string is null;
l_data.extend;
l_n := instr(l_string, ',');
l_data(l_data.count) := substr(l_string,1,l_n-1);
l_string := substr(l_string,l_n+1);
end loop;
return l_data;
end;
select * from table(cast(in_list('1,2,3,4,5') as myTcType));

How to calculate the sum of rows that have with the same id?

I need to create a function that returns the final gain of a product. My problem is that for every PRODUCT_ID that takes more than 10 days to be processed I have to add (days * 0.0001) to the product's price. Many ORDER_ID'S have different PRODUCT_ID. I can't think of a way to do that in the same function.Where do I have to add the multiplication? The database schema is like this
I tried double loop and I tried SUM but non of these worked.
CREATE OR REPLACE FUNCTION get_gain(orderID NUMBER)
RETURN NUMBER
AS
total_price NUMBER;
total_cost NUMBER;
gain NUMBER;
Cursor cur1 IS
SELECT SUM(PRICE) AS "Total PRICE"
FROM ORDERS
WHERE ORDER_ID=orderID;
Cursor cur2 IS
SELECT SUM(COST) AS "Total COST"
FROM ORDERS
WHERE ORDER_ID=orderID;
BEGIN
OPEN cur1;
FETCH cur1 INTO total_price;
CLOSE cur1;
OPEN cur2;
FETCH cur2 INTO total_cost;
CLOSE cur2;
gain := total_price - total_cost;
RETURN gain;
END;
you can use below approach to construct your query. I believe you won't need any function for this operation
SELECT
ORDER_ID,
...
CASE WHEN DAYS_TO_PROCESS > 10 THEN
PRICE + DAYS_TO_PROCESS * 0.0001
ELSE
PRICE
END AS PRICE
FROM table1
WHERE ...
It is not clear what you are trying to achieve. please provide more details.
Here is Function Example For updating product price
CREATE OR REPLACE FUNCTION UpdatePrice(orderID NUMBER)
RETURN NUMBER
AS
total_price NUMBER;
total_cost NUMBER;
gain NUMBER;
BEGIN
UPDATE table1
SET PRICE =
CASE WHEN DAYS_TO_PROCESS > 10 THEN
PRICE + DAYS_TO_PROCESS * 0.0001
ELSE
PRICE
END
WHERE ORDER_ID = p_ORDER_ID;
END;

Oracle: Use a cursor to check whether a record in one table exists in another

As a POC for my non-technical team I need to come up with several ways to do the same thing which is to check whether a record in one table exists in another in order to see which is the most efficient. I've come up with two other ways that I am positive will be more efficient than a cursor, but I still need to show the time it takes to do this in a cursor. I can't figure out the syntax however.
I have two tables:
Table 1 has two fields I need and that I am fetching into the variables in the cursor:
Field = ID
Field = Account number
Table 2 has one field I need:
Field = Account Number (No ID available)
I need the ID from table 1 and the count of transactions where the account number from table 1 is not in table 2. Any suggestions?
Given that sample data:
SQL> SELECT * FROM T1;
ID
--
1
2
3
SQL> SELECT * FROM T2;
ID
--
2
3
4
The proper way to perform an anti-join T1 ▷ T2 is by writing:
SQL> SELECT ID FROM T1 WHERE ID NOT IN (SELECT ID FROM T2);
ID
--
1
Now, if you want to do it using cursor to show how slow that can be, you might want to use two nested loops like in the following example:
DECLARE
CURSOR c1 IS (SELECT ID FROM T1);
r1 c1%ROWTYPE;
CURSOR c2 IS (SELECT ID FROM T2);
r2 c2%ROWTYPE;
BEGIN
FOR r1 IN c1
LOOP
FOR r2 IN c2
LOOP
IF (r1.ID = r2.ID)
THEN
-- continue to the next iteration of the outer loop
-- as we have a match
GOTO continue;
END IF;
END LOOP;
-- we can only reach that point if there was no match
DBMS_OUTPUT.PUT_LINE(TO_CHAR(r1.ID));
<<continue>>
NULL;
END LOOP;
END;
I'm not sure if I follow you, but a simple cursor might look like this:
DECLARE
v_id NUMBER;
v_acct NUMBER;
BEGIN
FOR r1 IN ( SELECT ID, ACCT_NBR
FROM table1 T1
WHERE NOT EXISTS ( SELECT 1 FROM table2
where ID = T1.ID )
) LOOP
v_id := r1.id;
v_acct := r1.acct_nbr;
-- do something
END LOOP;
end;

Resources