Oracle SQL: Compare all values from 2 columns and exchange them - sql

I have a Oracle DB with a table called myC. In this table I have a few row, two of them called myCheight, myCwidth.
I need to read these values and compare them like in IF myCheight > myCwidth DO switch the values.
I tried to read values from one row but didnt get it to work. I use Oracles Oracle SQL Developer.
This is what i came up with so far:
set serveroutput on;
DECLARE
cursor h is select * from MyC;
type htype is table of h%rowtype index by number;
stage_tab htype;
master_tab htype;
BEGIN
open h;
loop
fetch h bulk collect into stage_tab limit 500;
for i in 1 .. stage_tab.count loop
master_tab(stage_tab(i).id) := stage_tabe(i);
end loop;
exit when h%notfound;
end loop;
close h;
end;

Can't you just do this?
UPDATE myC
SET myCheight = myCwidth,
myCwidth = myCheight
WHERE myCheight > myCwidth

Related

How to get GET_QUERY_OPERATOR_STATS() for multiple queries?

The new Snowflake GET_QUERY_OPERATOR_STATS() allows me to get the stats for one query - but how do I get the stats for multiple queries?
https://docs.snowflake.com/en/sql-reference/functions/get_query_operator_stats.html
(asked by Yaron)
I wrote a SQL script to go through multiple query ids and store the results of GET_QUERY_OPERATOR_STATS() in a table.
First, create the table that will store the results:
create or replace table query_operator_stats_cache
as
select ''::string query_id, *
from table(get_query_operator_stats(last_query_id()))
-- just for the schema
limit 0;
Then you can go over a list of query ids:
declare
query_id string;
c1 cursor for
select query_id
from queries
where query_id not in (select query_id from query_operator_stats_cache);
begin
open c1;
for record in c1 do
fetch c1 into query_id;
insert into query_operator_stats_cache
select :query_id, * from table(get_query_operator_stats(:query_id));
end for;
return query_id;
end;
Go deeper on my post:
https://hoffa.medium.com/deep-performance-analysis-with-the-new-query-operator-stats-in-snowflake-74837971c5d3
It is possible to get multiple query profiles at once by generating query for multiple query_ids and executing it:
DECLARE
query TEXT;
rs RESULTSET;
BEGIN
SELECT LISTAGG(REPLACE($$SELECT * FROM TABLE(get_query_operator_stats('<query_id>'))$$,
'<query_id>', t.query_id), ' UNION ALL ') AS query
INTO query
FROM TABLE(information_schema.query_history(RESULT_LIMIT=>5)) AS t;
rs := (EXECUTE IMMEDIATE :query);
RETURN TABLE(rs);
END;

I want to make all the updates in pl sql without using update statement with the help of cursor for loop

I have create a big table named accounting_book I store data here from another table named Revenue
and now I want to update those records without using update statement with the help of cursor for loop in oracle database.
SET SERVEROUTPUT ON;
DECLARE
CURSOR SKC IS
SELECT TXNSEQ,TXNID,CASHAMT,CHECKAMT, PPAMT,CARDAMT
FROM REVENUE;
BEGIN
FOR a_REVENUE IN SKC LOOP
INSERT INTO ACCOUNTING_BOOK(TXNSEQ,TXNID,CASHAMT,
CHECKAMT,PPAMT,CARDAMT)
VALUES(a_REVENUE.TXNSEQ, a_REVENUE.TXNID, a_REVENUE.CASHAMT, a_REVENUE.CHECKAMT, a_REVENUE.PPAMT, a_REVENUE.CARDAMT);
END LOOP;
COMMIT;
END;
/
What should I do next please give me some suggetion...

Matching and updating two table

I am going to fetch every row from one table and find the equivalent in another table. Then i am going to update the rows of the second table by using the id which i have already gotten.
I tried to run my script but i had some problems.
I actually tried to make a loop and then put the id of every row in a variable to use them for my update statement but Pl shows me an error which tells me "not data found"
My unfinished script
DECLARE
tbl1Count number(4);
counter number(4);
MyO66ID number(8);
Begin
select Count(*) INTO tbl1Count from crbank ;
<<my_loop>>
For counter IN 1..tbl1Count-1 Loop
select O66ID INTO MyO66ID from crbank where rownum=counter;
End loop my_loop;
End;
You have written a strange logic in this scenario
This should work:
DECLARE
tbl1Count number(4) :=0;
MyO66ID number(8);
Begin
-- select Count(*) INTO tbl1Count from crbank; -- not needed at all
For myItems IN (select O66ID, ROWNUM, whatever_columns_you_need from crbank) Loop
MyO66ID := myItems.O66ID;
tbl1Count := tbl1Count + 1; -- this will serve you better than the first select if you are concerned of the number of rows you have.
/*
Do your logic here for the values you have in the myItems object
EX: update yourTable set yourColumn = myItems.otherColumn where id= myItems.something
You dont need variables to be defined if you noticed as in the above example.
*/
End loop;
End;
Hints:
You are getting the count, then looping on the count you get and matching it with rownum!, which is not a best practice; hitting your database twice, for count and for select, although you can do it in once loop, and no need for the first select
rownum will be different for each select statement, depending on the order you specified, so is it wise to use it?
You have mentioned in your question
I am going to fetch every row from one table and find the equivalent in another table
Oracle just have a workaround for this type of conditions. MERGE statement is very useful in these typical scenarios. Consider the below illustrated snippet. Let me know if this helps.
Whenever it is possible try to use pure SQL over PL/SQL
MERGE INTO <Update_table> USING <LOOKUP_TABLE>
ON
(UPDATE_TABLE.COLUMN_NAME = LOOKUP_TABLE.COLUMN_NAME)
WHEN MATCHED THEN
UPDATE SET
<UPDATE_TABLE.COLUMN_NAME> = <Update_value>
;
Try this one using cursor in sql.
Declare #id bigint
DECLARE CUR CURSOR FOR
select data from table1
open CUR
Fetch next from cur into #id
while ##FETCH_STATUS=0
begin
update table2 set columnname=value where id=#id
Fetch next from cur into #id
end
CLOSE CUR
DEALLOCATE CUR

foreach rows of my table and update my ROW_NUMBER column

I would like to create a script pl/sql where I can modify the value of my column ROW_NUMBER (the first time the value of ROW_NUMBER equal NULL).
This is the structure of my table 'A' :
CREATE TABLE A
(
"NAME" VARCHAR2(25 BYTE),
"NUM" NUMBER(10,0)
)
I would like to foreach all rows of table A and increment my Column 'NUM' by 1 if Column 'NAME' equal 'DEB'.
I would like to get the result like :
I created one pl/sql script :
DECLARE
INcrmt NUMBER(4):=1;
line WORK_ODI.TEST_SEQ%ROWTYPE;--before fetch it returns 0
CURSOR c_select IS
SELECT ROW_NUMBER,VALUE FROM WORK_ODI.TEST_SEQ;
BEGIN
OPEN c_select;
LOOP
FETCH c_select INTO line;
DBMS_OUTPUT.PUT_LINE(line.VALUE);
if line.VALUE like '%DEB%'
then
UPDATE WORK_ODI.TEST_SEQ SET ROW_NUMBER = INcrmt WHERE VALUE=line.VALUE;
INcrmt := INcrmt + 1;
end if;
if line.VALUE not like '%DEB%'
then
UPDATE WORK_ODI.TEST_SEQ SET ROW_NUMBER = INcrmt WHERE VALUE=line.VALUE;
end if;
EXIT WHEN c_select%NOTFOUND;
END LOOP;
CLOSE c_select;
COMMIT;
END;
DECLARE
INcrmt NUMBER(4):=1;
line WORK_ODI.TEST_SEQ%ROWTYPE;--before fetch it returns 0
CURSOR c_select IS
SELECT ROW_NUMBER,VALUE FROM WORK_ODI.TEST_SEQ;
BEGIN
OPEN c_select;
LOOP
FETCH c_select INTO line;
DBMS_OUTPUT.PUT_LINE(line.VALUE);
if line.VALUE like '%DEB%'
then
UPDATE WORK_ODI.TEST_SEQ SET ROW_NUMBER = INcrmt WHERE VALUE=line.VALUE;
INcrmt := INcrmt + 1;
end if;
if line.VALUE not like '%DEB%'
then
UPDATE WORK_ODI.TEST_SEQ SET ROW_NUMBER = INcrmt WHERE VALUE=line.VALUE;
end if;
EXIT WHEN c_select%NOTFOUND;
END LOOP;
CLOSE c_select;
COMMIT;
END;
but this is not work well , please take a look at what it gives me as result :
please anybody can help me
First, you should have an Aid column of some sort. In Oracle 12+, you can use an identity. In earlier versions, you can use a sequence. This provides an ordering for the rows in the table, based on insert order.
Second, you can do what you want on output:
select a.*,
sum(case when a.name like 'DEB%' then 1 else 0 end) over (order by aid) as row_number
from a;
If you really need to keep the values in the table, then you can use a merge statement to assign values to existing rows (the aid column is very handy for this). You will need a trigger afterwards to maintain it.
My suggestion is to do the calculation on the data, rather than storing the value in the data. Maintaining the values with updates and deletes seems like a real pain.

How do I use bulk collect and insert in Pl/SQl

I want to fetch around 6 millions rows from one table and insert them all into another table.
How do I do it using BULK COLLECT and FORALL ?
declare
-- define array type of the new table
TYPE new_table_array_type IS TABLE OF NEW_TABLE%ROWTYPE INDEX BY BINARY_INTEGER;
-- define array object of new table
new_table_array_object new_table_array_type;
-- fetch size on bulk operation, scale the value to tweak
-- performance optimization over IO and memory usage
fetch_size NUMBER := 5000;
-- define select statment of old table
-- select desiered columns of OLD_TABLE to be filled in NEW_TABLE
CURSOR old_table_cursor IS
select * from OLD_TABLE;
BEGIN
OPEN old_table_cursor;
loop
-- bulk fetch(read) operation
FETCH old_table_cursor BULK COLLECT
INTO new_table_array_object LIMIT fetch_size;
EXIT WHEN old_table_cursor%NOTFOUND;
-- do your business logic here (if any)
-- FOR i IN 1 .. new_table_array_object.COUNT LOOP
-- new_table_array_object(i).some_column := 'HELLO PLSQL';
-- END LOOP;
-- bulk Insert operation
FORALL i IN INDICES OF new_table_array_object SAVE EXCEPTIONS
INSERT INTO NEW_TABLE VALUES new_table_array_object(i);
COMMIT;
END LOOP;
CLOSE old_table_cursor;
End;
Hope this helps.
oracle
Below is an example From
CREATE OR REPLACE PROCEDURE fast_way IS
TYPE PartNum IS TABLE OF parent.part_num%TYPE
INDEX BY BINARY_INTEGER;
pnum_t PartNum;
TYPE PartName IS TABLE OF parent.part_name%TYPE
INDEX BY BINARY_INTEGER;
pnam_t PartName;
BEGIN
SELECT part_num, part_name
BULK COLLECT INTO pnum_t, pnam_t
FROM parent;
FOR i IN pnum_t.FIRST .. pnum_t.LAST
LOOP
pnum_t(i) := pnum_t(i) * 10;
END LOOP;
FORALL i IN pnum_t.FIRST .. pnum_t.LAST
INSERT INTO child
(part_num, part_name)
VALUES
(pnum_t(i), pnam_t(i));
COMMIT;
END
The SQL engine parse and executes the SQL Statements but in some cases ,returns data to the PL/SQL engine.
During execution a PL/SQL statement, every SQL statement cause a context switch between the two engine. When the PL/SQL engine find the SQL statement, it stop and pass the control to SQL engine. The SQL engine execute the statement and returns back to the data in to PL/SQL engine. This transfer of control is call Context switch. Generally switching is very fast between PL/SQL engine but the context switch performed large no of time hurt performance .
SQL engine retrieves all the rows and load them into the collection and switch back to PL/SQL engine. Using bulk collect multiple row can be fetched with single context switch.
Example : 1
DECLARE
Type stcode_Tab IS TABLE OF demo_bulk_collect.storycode%TYPE;
Type category_Tab IS TABLE OF demo_bulk_collect.category%TYPE;
s_code stcode_Tab;
cat_tab category_Tab;
Start_Time NUMBER;
End_Time NUMBER;
CURSOR c1 IS
select storycode,category from DEMO_BULK_COLLECT;
BEGIN
Start_Time:= DBMS_UTILITY.GET_TIME;
FOR rec in c1
LOOP
NULL;
--insert into bulk_collect_a values(rec.storycode,rec.category);
END LOOP;
End_Time:= DBMS_UTILITY.GET_TIME;
DBMS_OUTPUT.PUT_LINE('Time for Standard Fetch :-' ||(End_Time-Start_Time) ||' Sec');
Start_Time:= DBMS_UTILITY.GET_TIME;
Open c1;
FETCH c1 BULK COLLECT INTO s_code,cat_tab;
Close c1;
FOR x in s_code.FIRST..s_code.LAST
LOOP
null;
END LOOP;
End_Time:= DBMS_UTILITY.GET_TIME;
DBMS_OUTPUT.PUT_LINE('Using Bulk collect fetch time :-' ||(End_Time-Start_Time) ||' Sec');
END;
CREATE OR REPLACE PROCEDURE APPS.XXPPL_xxhil_wrmtd_bulk
AS
CURSOR cur_postship_line
IS
SELECT ab.process, ab.machine, ab.batch_no, ab.sales_ord_no, ab.spec_no,
ab.fg_item_desc brand_job_name, ab.OPERATOR, ab.rundate, ab.shift,
ab.in_qty1 input_kg, ab.out_qty1 output_kg, ab.by_qty1 waste_kg,
-- null,
xxppl_reports_pkg.cf_waste_per (ab.org_id,ab.process,ab.out_qty1,ab.by_qty1,ab.batch_no,ab.shift,ab.rundate) waste_percentage,
ab.reason_desc reasons, ab.cause_desc cause,
to_char(to_date(ab.rundate),'MON')month,
to_char(to_date(ab.rundate),'yyyy')year,
xxppl_org_name (ab.org_id) plant
FROM (SELECT a.org_id,
(SELECT so_line_no
FROM xx_ppl_logbook_h x
WHERE x.batch_no = a.batch_no
AND x.org_id = a.org_id) so_line_no,
(SELECT spec_no
FROM xx_ppl_logbook_h x
WHERE x.batch_no = a.batch_no
AND x.org_id = a.org_id
and x.SALES_ORD_NO=a.sales_ord_no
and x.OPRN_ID =a.OPRN_ID
and x.RESOURCES = a.RESOURCES) SPEC_NO,
(SELECT OPERATOR
FROM xx_ppl_logbook_f y, xx_ppl_logbook_h z
WHERE y.org_id = a.org_id
AND y.batch_no = a.batch_no
AND y.rundate = a.rundate
AND a.process = y.process
AND a.resources = y.resources
AND a.oprn_id = y.oprn_id
AND z.org_id = y.org_id
AND z.batch_no = y.batch_no
AND z.batch_id = z.batch_id
AND z.rundate = y.rundate
AND z.process = y.process) OPERATOR,
a.batch_no, a.oprn_id, a.rundate, a.fg_item_desc,
a.resources, a.machine, a.sales_ord_no, a.process,
a.process_desc, a.tech_desc, a.sub_invt, a.by_qty1,
a.by_qty2, a.um1, a.um2, a.shift,
DECODE (shift, 'I', 1, 'II', 2, 'III', 3) shift_rank,
a.reason_desc, a.in_qty1, a.out_qty1, a.cause_desc
FROM xxppl_byproduct_waste_v a
WHERE 1 = 1
--AND a.org_id = (CASE WHEN :p_orgid IS NULL THEN a.org_id ELSE :p_orgid END)
AND a.rundate BETWEEN to_date('01-'||to_char(add_months(TRUNC(TO_DATE(sysdate,'DD-MON-RRRR')) +1, -2),'MON-RRRR'),'DD-MON-RRRR') AND SYSDATE
---AND a.process = (CASE WHEN :p_process IS NULL THEN a.process ELSE :p_process END)
) ab;
TYPE postship_list IS TABLE OF xxhil_wrmtd_tab%ROWTYPE;
postship_trns postship_list;
v_err_count NUMBER;
BEGIN
delete from xxhil_wrmtd_tab;
OPEN cur_postship_line;
FETCH cur_postship_line
BULK COLLECT INTO postship_trns;
FORALL i IN postship_trns.FIRST .. postship_trns.LAST SAVE EXCEPTIONS
INSERT INTO xxhil_wrmtd_tab
VALUES postship_trns (i);
CLOSE cur_postship_line;
COMMIT;
END;
/
---- INSERING DATA INTO TABLE WITH THE HELP OF CUROSR