Using HSQL 2.2.5 I need to shudder process one row at a time in a stored procedure, so I thought the "Iterated FOR" statement might do the trick for me. Unfortunately I don't seem to be able to make it work. It's supposed to look something like:
FOR SELECT somestuff FROM sometable DO
some random SQL statements
END FOR;
That leaves off a bit of the syntax, but it's close enough for now.
The problem seems to be that the statements inside the loop never execute. I've verified that my SELECT statement does indeed return something.
So let's get concrete. When I execute this stored procedure:
CREATE PROCEDURE b()
MODIFIES SQL DATA
BEGIN ATOMIC
DECLARE count_var INTEGER;
SET count_var = 0;
WHILE count_var < 10 DO
INSERT INTO TTP2 VALUES(count_var);
SET count_var = count_var + 1;
END WHILE;
END;
I get 10 rows inserted into table TTP2, with values 0 through 9. (TTP2 has just one column defined, of type INTEGER.)
But when I substitute a FOR statement for the WHILE like so:
CREATE PROCEDURE c()
MODIFIES SQL DATA
BEGIN ATOMIC
DECLARE count_var INTEGER;
SET count_var = 0;
FOR SELECT id FROM ttp_by_session FETCH 10 ROWS ONLY DO
INSERT INTO TTP2 VALUES(count_var);
SET count_var = count_var + 1;
END FOR;
END;
I get nothing inserted into TTP2. (I have verified that the SELECT statement returns 10 rows, one column of integers.)
When I leave the FETCH clause off I still get no results. ttp_by_session is a view, but the same thing happens with a bare table.
What am I doing wrong?
Thanks for the help.
This works fine with the latest version of HSQLDB. Try with the 2.3.0 release candidate snapshot from the HSQLDB web site.
When the FOR statement was initially added about two years ago, it had limited functionality. The functionality was extended in later versions.
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 will like to ask a performance related question here my question is which approach is best
1 adding subquery in loop
declare
test varchar2(50);
FOR Lcntr IN 1..20
LOOP
update emp set empno='50' where empname=test;
END LOOP;
2 adding function call in loop or making the function of the above query and calling it in loop
declare
test varchar2(50);
FOR Lcntr IN 1..20
LOOP
temp:=update('argument');
END LOOP;
If your function update just call the same sql update, it dosen't matter are you call it directly of from stored function.
In common the best way is use one sql statement (update or merge) for update whole dataset what you need.
But you update are look like strange:
In first pl/sql block you declare variable test. And test is equal null. And after that you try update table by comparing with null - no any rows will be affected.
In second pl\sql block you declare variable test too, but use varable temp. It will raise error in compilation.
I'm currently learning SQL and I'm having trouble with a procedure of mine. The procedure should calculate the average of a column called 'INDICE_METABO_PAT'. The information I need is in 3-4 different tables. Then when I do have the average calculated, I update a table to set this average to the corresponding entries. Here is the procedure. Note that everything else in my .sql file works : inserts, updates, selects, views, etc.
create or replace Procedure SP_UPDATE_INDICE_METABO_DV (P_NO_ETUDE in number)
is
V_SOMME number := 0;
V_NBPATIENT number := 0;
V_NO_ETUDE number := P_NO_ETUDE;
cursor curseur is
select PATIENT.INDICE_EFFICACITE_METABO_PAT
from ETUDE, DROGUE_VARIANT, ETUDE_PATIENT, PATIENT
where ETUDE.NO_DROGUE = DROGUE_VARIANT.NO_DROGUE
and ETUDE.NO_VAR_GEN = DROGUE_VARIANT.NO_VAR_GEN
and V_NO_ETUDE = ETUDE_PATIENT.NO_ETUDE
and ETUDE_PATIENT.NO_PATIENT = PATIENT.NO_PATIENT;
begin
open curseur;
fetch curseur into V_SOMME;
V_NBPATIENT := V_NBPATIENT + 1;
exit when curseur%NOTFOUND;
update DROGUE_VARIANT
set INDICE_EFFICACITE_METABO_DV = V_SOMME / V_NBPATIENT
where exists(select * from ETUDE, DROGUE_VARIANT, ETUDE_PATIENT, PATIENT
where ETUDE.NO_DROGUE = DROGUE_VARIANT.NO_DROGUE
and ETUDE.NO_VAR_GEN = DROGUE_VARIANT.NO_VAR_GEN
and V_NO_ETUDE = ETUDE_PATIENT.NO_ETUDE
and ETUDE_PATIENT.NO_PATIENT = PATIENT.NO_PATIENT);
end SP_UPDATE_INDICE_METABO_DV;
/
I'm getting an error : Procedure compiled , error check compiler log.
But I can't open the compiler log, and when my friend opens it, it points to weird places, like my create tables and such.
This is school stuff by the way, so it'll be nice if you could give an insight instead of a direct solution. Thanks alot.
Thanks alot in advance for your kind help !
To see the error you can do show errors after your procedure creation statement, or you can query the user_errors or all_errors views.
That will show something like:
LINE/COL ERROR
-------- ------------------------------------------------------------------------
20/4 PLS-00376: illegal EXIT/CONTINUE statement; it must appear inside a loop
20/4 PL/SQL: Statement ignored
You mentioned that when you checked the complier log, which shows the same information, "it points to weird places". Presumably you're looking at line 20 in your script. But this message is referring to line 20 of the PL/SQL code block, which is the exit when curseur%NOTFOUND;, which makes sense for the error message.
And as the message also says, and as #ammoQ said in a comment, that should be in a loop. If you're trying to manually calculate the average in a procedure as an exercise, instead of using the built-in aggregation functions, then you need to loop over all of the rows from your cursor:
open curseur;
loop
fetch curseur into V_SOMME;
exit when curseur%NOTFOUND;
V_NBPATIENT := V_NBPATIENT + 1;
end loop;
close curseur;
But as you'll quickly realise, you'll end up with the v_somme variable having the last value retrieved, not the sum of all the values. You need a separate variable keep to track of the sum - fetch each value into a variable, and add that to your running total, all within the loop. But as requested, not giving a complete solution.
As you're starting out you should really use ANSI join syntax, not the old from/where syntax you have now. It's a shame that is still being taught. So your cursor query should be something like:
select PATIENT.INDICE_EFFICACITE_METABO_PAT
from ETUDE_PATIENT
join ETUDE
-- missing on clause !
join DROGUE_VARIANT
on DROGUE_VARIANT.NO_DROGUE = ETUDE.NO_DROGUE
and DROGUE_VARIANT.NO_VAR_GEN = ETUDE.NO_VAR_GEN
join PATIENT
on PATIENT.NO_PATIENT = ETUDE_PATIENT.NO_PATIENT
where ETUDE_PATIENT.NO_ETUDE = P_NO_ETUDE;
... which shows you that you are missing a join condition between ETUDE_PATIENT and ETUDE - it's unlikely you want a cartesian product, and it's much easier to spot that missing join using this syntax than with what you had.
You need to look at your update statement carefully too, particularly the exists clause. That will basically always return true if the cursor found anything, so it will update every row in DROGUE_VARIANT with your calculated average, which presumably isn't what you want.
There is no correlation between the rows in the table you're updating and the subquery in that clause - the DROGUE_VARIANT in the subquery is independent of the DROGUE_VARIANT you're updating. By which I mean, it's the same table, obviously; but the update and the subquery are looking at the table separately and so are looking at different rows. It also has the same missing join condition as the cursor query.
I am trying to create a PL/SQL procedure which needs to do a for loop through a cursor. I have read in the oracle forums that PL/SQL 8i doesn't support subscalar queries.
This is what I have so far :
DECLARE
CURSOR C1
IS
SELECT texto,id_evento,clave_evento FROM gegf.eventos_omega_rima WHERE id_evento IN
(select max(eo.id_evento) from gegf.eventos_omega_rima eo, correctivo_rima.equipos b
where eo.fecha_ins_tab > sysdate - 25/24 and eo.fecha_ins_tab < sysdate - 1/24 and upper(eo.ORIGEN) = upper(b.nodo) and upper(b.red) = 'RIMA' group by eo.clave_evento);
r_emp C1%ROWTYPE;
BEGIN
OPEN C1;
LOOP
FETCH c1 INTO r_emp;
EXIT WHEN C1%NOTFOUND;
INSERT INTO CORRECTIVO_RIMA.T_CLOB VALUES (r_emp.TEXTO);
END LOOP;
CLOSE c1;
END;
/
How could I workaround the fact that I can't use subscalar queries in the PL/SQL version I am using?
The PLS-00103 is telling you where the problem is; line 6 column 49. In this part of your query:
where eo.fecha_ins_tab > sysdate - and
... something is missing after the minus sign; presumably you're trying to subtract some number of days from today, but you haven't supplied that number.
I don't have an 8i database lying around any more (perhaps not surprisingly) but I don't recall ever needing to quote a cursor query; and if you do I'm pretty sure the semicolon would need to be outside the closing quote. But that was also what was causing the earlier line 4, column 5 error, which was pointing at that opening quote.
You will also try to insert the last value twice; you need to test C1%NOTFOUND before the INSERT, immediately after the FETCH (unless you are using bulk collect). Of course you're inserting a dummy value, but you'll get one too many rows; with your real CLOB you'd process the last fetch value twice.
I need to run big queries (that was a part of SP) and look at their results (just trying to find a bug in a big SP with many unions. I want to break it into parts and run them separately).
How can I do that if this SP have few parameters? I don't want to replace them in code, it would be great just to add declare in a header with a hardcode for this parameter.
I've tried something like this:
DECLARE
p_asOfDate DATE := '22-Feb-2011';
BEGIN
SELECT * from myTable where dateInTable < p_asOfDate;
END
But it says that I should use INTO keyword. How can I view this results in my IDE? (I'm using Aqua data studio)
I need to do that very often, so will be very happy if will find a simple solution
You are using an anonymous block of pl/sql code.
In pl/sql procedures you need to specify a target variable for the result.
So you first need to define a variable to hold the result in the declare section
and then insert the result data into it.
DECLARE
p_asOfDate DATE := '22-Feb-2011';
p_result myTable%ROWTYPE;
BEGIN
select * into p_result from myTable where dateInTable < p_asOfDate;
END
That said you will probaply get more than one row returned, so I would use
a cursor to get the rows separately.
DECLARE
CURSOR c_cursor (asOfDate IN DATE) is
select * from myTable where dateInTable < asOfDate;
p_asOfDate DATE := '22-Feb-2011';
p_result myTable%ROWTYPE;
BEGIN
OPEN c_cursor(p_asOfDate);
loop
FETCH c_cursor into p_result;
exit when c_cursor%NOTFOUND;
/* do something with the result row here */
end loop;
CLOSE c_cursor;
END
To output the results you can use something like this for example:
dbms_output.put_line('some text' || p_result.someColumn);
Alternatively you can execute the query on an sql command-line (like sqlplus)
and get the result as a table immediately.
I hope I understood your question correctly...
update
Here is a different way to inject your test data:
Use your tools sql execution environemnt to submit your sql statement directly without a pl/sql block.
Use a "&" in front of the variable part to trigger a prompt for the variable.
select * from myTable where dateInTable < &p_asOfDate;
The Result should be displayed in a formatted way by your tool this way.
I do not know about Aqua, but some tools have functions to define those parameters outside the sql code.