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
Related
I am quite new to sql and have been trying to work on the following script to parameterize it.
This is my code:
select
dc.deviceid,
dc.kernel_time,
dc.crash_time,
dc.crash_process,
dps.start_time,
dps.end_time,
dps.start_kernel_time,
dps.end_kernel_time,
case
when dc.kernel_time between dps.start_kernel_time and dps.end_kernel_time then 1
when dc.crash_time between dps.start_time and dps.end_time then 2
else 3
end as flag,
ROW_NUMBER () over (partition by dc.deviceid, dc.kernel_time,
dc.crash_time, dc.crash_process order by flag) row_num
from dummy.dummy_crashes dc
left outer join (select *
from dummy.dummy_power) as dps
on dc.deviceid = dps.deviceid
and ((dc.kernel_time between (dps.start_kernel_time + 10000) and (dps.end_kernel_time + 10000)) or (dc.crash_time between dps.start_time and dps.end_time))
order by dc.crash_time;
I need to test this script by changing the start_kernel_time and end_kernel_time with a certain int parameter value (in this example shown: 10000) every time. So, instead of modifying it in the code, I would like to create a function with the int parameter of choice and run this script. Would that be possible?
I am really clueless as to how to achieve that.
My ideal idea would be something like this:
get_crashes(10000); <-- get records with adding int parameter (in start_kernel_time and end_kernel_time) as 10000
get_crashes(30000); <-- get records with adding int parameter as 30000
get_crashes(80000); <-- get records with adding int parameter as 80000
I am really trying to understand how I could achieve this?
I can't write a comment because i don't have 50rep, but here is my answer:
You can create a temp table with values that you want to pass and call cursor with simple query like:
SELECT [value] FROM *temptable*
After that, inside cursor just write script with single value from above temp table
UPDATE
DECLARE
cur CURSOR FOR select col1 from tempTable;
test_cur RECORD;
BEGIN
open cur;
LOOP
fetch cur into test_cur;
exit when test_cur = null;
if test_cur.col1 IS NOT NULL then
return next test_cur.col1;
end if;
END LOOP;
close cur;
END;
One note - I never write PostgreSQL, just have knowledge about SQL and find code on internet, so maybe you need to check documentation.
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.
I am attempting to write an Oracle stored procedure that will call five (5) sub-stored procedures for all of the Bill's (bill_id_tab) in a particular result set. I am using a FOR loop, and the only set of data being returned is the very last Bill. I am assuming that it is writing out the data AFTER the FOR..LOOP has completed, and not during. I am not sure how to get it to write out each resultset during the processing.
Here is my code:
BEGIN
DECLARE
v_bill_id CHAR(12);
v_batch_nbr NUMBER(10);
--define cursor
cursor bill_id_tab is (SELECT BILL_ID
FROM CI_BILL_ROUTING
WHERE BATCH_NBR = (SELECT MAX(BATCH_NBR)
FROM CI_BILL_ROUTING
WHERE BATCH_CD = 'DGRTPOST')
AND BATCH_CD = 'DGRTPOST')
ORDER BY BILL_ID;
BEGIN
/******************************************************************************
get bill_id's for all bills generated in the max Batch_nbr.
Batch_cd will be used to seperate
******************************************************************************/
BEGIN
FOR each BILL_ID IN bill_id_tab LOOP
BEGIN
RPTUSER.CM_DGBILLPB_HP2(p_info_cursor1, NULL, rec.BILL_ID, NULL, NULL);
RPTUSER.CM_DGBILLPR_HP(p_info_cursor2, rec.BILL_ID);
RPTUSER.CM_DGBILLMS_HP(p_info_cursor3, rec.BILL_ID, 'C');
RPTUSER.CM_DGBILLCG_HP(p_info_cursor4, rec.BILL_ID);
RPTUSER.CM_DGBILLMR_HP(p_info_cursor5, rec.BILL_ID);
END;
END LOOP;
END;
END;
The bills that I would expect to see data for is:
819244555299
819246299764
819248391148
951891843093
951896713417
951897314849
The only resultset that is returned is Bill_ID - 951897314849.
Any guidance will be appreciated.
Thanks in advance.
declare
CURSOR C1
IS select tgt.exp_date ,(src.eff_date - 1/(24*60*60))eff_date
from mira_rate tgt,mira_rate_dummy src
where src.tc_code = tgt.tc_code and src.carrier_code = tgt.carrier_code and tgt.exp_date is null for update of tgt.exp_date;
v_a date;
v_b date;
i number:=0;
begin
open c1;
loop
fetch c1 into v_a, v_b;
exit when c1%notfound;
update mira_rate
set exp_date =v_b where current of c1;
i:=i+1;
end loop;
dbms_output.put_line(i||' rows updated');
close c1;
commit;
end;
After i excecute the query it is locking the table says
ORA-00054: resource busy and acquire with NOWAIT specified
Also pls tell me how to remove the lock i tried killing the sesssion it is not happening.still it says the same
Affter removing the lock. Pls clear me this requirement
select tgt.exp_date ,(src.eff_date - 1/(24*60*60))eff_date
from mira_rate tgt,mira_rate_dummy src
where src.tc_code = tgt.tc_code and src.carrier_code = tgt.carrier_code and tgt.exp_date is null;
it ill return rows I need to goto the mira_rate table need to update exp_date=eff_date.
Please suggest me how to do i m using Oracle 9i so merge without not matched is working
At first sight, there is no commit in the code.
The code with commit wil be ok. Commit will release the locks(Oracle cursor examples/expl)
But better you would:
MERGE INTO mira_rate tgt
USING mira_rate_dummy src
ON (src.tc_code = tgt.tc_code and src.carrier_code = tgt.carrier_code)
WHEN MATCHED THEN UPDATE
SET exp_date= src.eff_date - 1/(24*60*60) --or just src.eff_date
WHERE tgt.exp_date is null;
This is what you want to do as far as I understand.
As a rule: What you can do in SQL, do in SQL, not PL/SQL.
Take out the 'FOR UPDATE'.
You need to be very clear in your mind why you need it and in my experience you generally don't.
Between us I think we are saying this should be your approach
begin
UPDATE mira_rate
SET exp_date= src.eff_date - 1/(24*60*60)
WHERE exp_date is null;
DBMS_OUTPUT.PUT_LINE
(TO_CHAR(SQL%ROWCOUNT) || ' Rows Updated);
end;
No need for locks and no need for cursors.
Hope that helps.
Edit - still not entirely sure what your requirement is but the following sql may be what you are looking for.
UPDATE MIRA_RATE TGT
SET EXP_DATE =
(
SELECT SRC.EFF_DATE - 1/86400
FROM MIRA_RATE_DUMMY SRC
WHERE
SRC.TC_CODE = TGT.TC_CODE AND
SRC.CARRIER_CODE = TGT.CARRIER_CODE
)
WHERE
TGT.EXP_DATE IS NULL;
#Satheesh, Updatable select will work only for primary key columns. See if the select fetches the primary key and also useins it in where clause. Else the update will throw error.
There is something to check for
cannot modify a column which maps to a non key-preserved table
You can have join but the update needs primary key to update in the base table.
I have a stored procedure which does not need to return any values. It runs smoothly and without any problem. However, it outputs an error message after finishing its run:
Error: No data - zero rows fetched, selected, or processed
How can I get rid of this error message?
CREATE PROCEDURE `testing_proc`()
READS SQL DATA
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE l_name VARCHAR(20);
DECLARE my_cur CURSOR FOR
SELECT name FROM customer_tbl;
OPEN my_cur;
my_cur_loop:
LOOP FETCH my_cur INTO l_name;
IF done = 1 THEN
LEAVE my_cur_loop;
END IF;
INSERT INTO names_tbl VALUES(l_name);
END LOOP my_cur_loop;
CLOSE my_cur;
END
I guess you just forgot to include the following line in your post:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
Your code is correct, but bug/strange behaviour of mysql causes the warning to appear even if it was handled. You can avoid that if you add a "dummy" statement to the end of your procedure that invovles a table and is successful, this will clear the warning. (See http://dev.mysql.com/doc/refman/5.5/en/show-warnings.html)
In your case:
SELECT name INTO l_name FROM customer_tbl LIMIT 1;
after the end of the loop.
On MySQL 5.5.13 the warning disappears, on Linux and Windows.
I commented on MySQL Bug 60840 and I hope they will fix it some time in the future...
You need to define a continue handler like:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
So it would look like:
DECLARE done INT DEFAULT 0;
DECLARE l_name VARCHAR(20);
DECLARE my_cur CURSOR FOR
SELECT name FROM customer_tbl;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN my_cur;
my_cur_loop:
LOOP FETCH my_cur INTO l_name;
IF done = 1 THEN
LEAVE my_cur_loop;
END IF;
INSERT INTO names_tbl VALUES(l_name);
END LOOP my_cur_loop;
CLOSE my_cur;
I ran into this and pulled out my hair till I ran across this in the official mysql docs
Before MySQL 5.6.3, if a statement that generates a warning or error
causes a condition handler to be invoked, the handler may not clear
the diagnostic area. This might lead to the appearance that the
handler was not invoked. The following discussion demonstrates the
issue and provides a workaround.
Click the link and scroll to the bottom for details but the fix was to include a successful select INSIDE the CONTINUE HANDLER:
DECLARE CONTINUE HANDLER FOR NOT FOUND
BEGIN
SELECT 1 INTO #handler_invoked FROM (SELECT 1) AS t;
END;
I tried the solutions in here and none, including the continue handler worked for me. I still get the messages in the MySQL error log. I discovered this also with my "select ... into ..." which made sense, but I really thought the continue handler would work for the cursors. Either way I found using "found_rows()" to find out if any rows were returned worked perfectly. This mean that the simple "select into" statements have to be converted to cursors, but it isn't much work and does solve the problem.
DECLARE v_rowcount integer unsigned;
DECLARE cur_entries cursor for
select app_name, proc_name, error_code, sum(occurrences) occurrences
from that_table...;
open cur_entries;
set v_rowcount = found_rows();
if v_rowcount > 0 then
fetch cur_entries into v_app_name, v_proc_name, v_error_code, v_occurrences;
...
end if;
close cur_entries;
I wrote this up on my personal blog here: http://tinky2jed.wordpress.com/technical-stuff/mysql/mysql-no-data-zero-rows-fetched-how-to-code-for-it/
Normally this happens when you overshoot a cursor range, so checkout the loop conditions where the FETCH statement is
I don't know if this fixes the cursor issue, but I ran into this warning with a stored function and found that if you use:
RETURN (SELECT x From myTable...);
instead of
SELECT x into myVar...return myVar
I got this from this helpful doc:
http://bugs.mysql.com/bug.php?id=42834
I was getting the same error in my code, and I realized that I had not incremented my loop variable (using while loop) and hence, the loop was going infinite.
In your code too, you are not setting "done" to 1 anywhere, and I think the code is showing error because of that.
In the below code, instead of the variable "done", I have added a variable "count" that is initialized with the number of records in the table and is decremented after each insertion. The loop is terminated when count=0:
CREATE PROCEDURE `testing_proc`()
READS SQL DATA
BEGIN
DECLARE count INT;
DECLARE l_name VARCHAR(20);
SELECT count(*) into count from customer_tbl;
DECLARE my_cur CURSOR FOR
SELECT name FROM customer_tbl;
OPEN my_cur;
my_cur_loop:
LOOP FETCH my_cur INTO l_name;
INSERT INTO names_tbl VALUES(l_name);
SET count = count - 1;
IF count = 0 THEN
LEAVE my_cur_loop;
END IF;
END LOOP my_cur_loop;
CLOSE my_cur;
END
I hope this helps!