I get this error
23/112 PL/SQL: ORA-00920: invalid relational operator
It's pointing to AND CURRENT OF statement..
CREATE OR replace PROCEDURE alga_uz_pasirodyma(grupe_id in grupes.id%TYPE, alga in out number)
IS
v_kliento_id nariai.asm_kodas%TYPE;
TYPE bendras IS RECORD (
alga number
);
globalus bendras;
CURSOR c_klientai IS
SELECT nariai.asm_kodas
FROM nariai
where nariai.fk_grupe = grupe_id
FOR UPDATE OF nariai.alga;
BEGIN
globalus.alga:= alga;
IF grupe_id <= 0 THEN
raise_application_error(-20101, 'Nepavyko surasti grupes');
END IF;
OPEN c_klientai;
LOOP
FETCH c_klientai INTO v_kliento_id;
EXIT WHEN c_klientai%NOTFOUND;
UPDATE nariai set nariai.alga = nariai.alga * globalus.alga where nariai.asm_kodas = v_kliento_id AND CURRENT OF c_klientai;
END LOOP;
UPDATE grupes set grupes.pasirodymu_kiekis = grupes.pasirodymu_kiekis + 1 where grupes.id = grupe_id;
SELECT max(nariai.alga) into alga from nariai where nariai.fk_grupe = grupe_id;
CLOSE c_klientai;
END alga_uz_pasirodyma;
What should I do? I believe everything is declared correctly in the where statement..
"CURRENT OF" should be by itself in the where clause. It allows you to update or delete the record at the current loop iteration of the cursor.
On a different note, I don't see you doing anything significant in the loop to warrant a cursor. Ignore this note if that will change, otherwise just run the update "where nariai.fk_grupe = grupe_id"
This Procedure is compiling fine but taking forever to run. I think even for single row, it is taking too much time to process. Where should I change the code to make it run faster?
CREATE OR REPLACE PROCEDURE NEWWAPTWOPROC(
P_WAP NUMBER,
P_DEALNO VARCHAR2,
P_FDT DATE,
P_SECURITYCD VARCHAR2,
P_SECURITYTYPE VARCHAR2,
P_SELFORCONSTITUENT VARCHAR2
)
IS
v_pofv NUMBER(30);
v_sofv NUMBER(30);
CURSOR C1 IS
SELECT
D.SECURITYCD SCD,
D.DEALNO DNO,
S.OWNSTK OWNST,
LAG(S.OWNSTK) OVER(ORDER BY D.DEALDT) PC_STK,
D.Dealtype DTYPE,
D.SECURITYTYPE STYPE,
D.SELFORCONSTITUENT SFORCO,
D.DEALDT DDATE ,
D.PRICE PRC,
D.FACEVALUE FV,
LAG(D.FACEVALUE) OVER(ORDER BY D.DEALDT) PC_FV,
D.WAP1 WP,
LAG(D.WAP1) OVER(ORDER BY D.DEALDT) PC_WAP1
FROM MMDEAL0_NWAP D INNER JOIN Mmstock1 S
ON D.Securitycd=S.Securitycd
WHERE D.DEALDT>=P_FDT
AND D.DEALNO=P_Dealno
AND D.FACEVALUE>0
AND D.Dealtype IN('PO','SO')
AND D.Selforconstituent='S'
AND D.SECURITYTYPE='DGS'
ORDER BY
D.DEALDT,D.Securitycd;
BEGIN
FOR i in C1
LOOP
if C1%rowcount=1 then
UPDATE MMDEAL0_NWAP SET WAP1=P_WAP WHERE Dealno=P_DEALNO AND DEALDT=P_FDT AND
SECURITYCD=P_SECURITYCD AND SECURITYTYPE=P_SECURITYTYPE AND
Selforconstituent=P_SELFORCONSTITUENT;
else
IF i.OWNST>0 then
if i.DTYPE='PO' then
v_pofv:=i.FV;
elsif i.DTYPE='SO' then
v_sofv:=i.FV;
end if;
i.WP:=((nvl(i.PC_WAP1,0)*nvl(i.PC_STK,0))+(nvl(v_pofv,0)*nvl(i.PRC,0)))-(nvl(v_sofv,0)*nvl(i.PC_WAP1,0))/i.OWNST;
UPDATE MMDEAL0_NWAP SET WAP1=i.WP WHERE DEALNO=i.DNO AND DEALDT=i.DDATE AND
SECURITYCD=i.SCD AND SECURITYTYPE=i.STYPE AND SELFORCONSTITUENT= i.SFORCO;
END IF;
end if;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
Dbms_Output.Put_Line(SQLCODE||' '||SQLERRM);
END NEWWAPTWOPROC;
You are using CURSOR and loops for no reason when all your updates can be converted to an update and a MERGE statement.
This below is your update statement for the case C1%rowcount=1, which was not required to be put inside the loop even before.
UPDATE mmdeal0_nwap
SET wap1 = p_wap
WHERE dealno = p_dealno
AND dealdt = p_fdt
AND securitycd = p_securitycd
AND securitytype = p_securitytype
AND selforconstituent = p_selforconstituent;
The second update converted to MERGE
MERGE INTO mmdeal0_nwap t USING (
SELECT i.*,
( ( nvl(i.pc_wap1,0) * nvl(i.pc_stk,0) ) + ( nvl(v_pofv,0) * nvl(i.prc,0) ) ) - ( nvl(v_sofv
,0) * nvl(i.pc_wap1,0) ) / i.ownst AS new_wp --your calculation for the value of WP to be updated
FROM (
SELECT d.securitycd scd,
d.dealno dno,
s.ownstk ownst,
LAG(s.ownstk) OVER(
ORDER BY d.dealdt
) pc_stk,
d.dealtype dtype,
d.securitytype stype,
d.selforconstituent sforco,
d.dealdt ddate,
d.price prc,
d.facevalue fv,
LAG(d.facevalue) OVER(
ORDER BY d.dealdt
) pc_fv,
d.wap1 wp,
LAG(d.wap1) OVER(
ORDER BY d.dealdt
) pc_wap1,
CASE
WHEN d.dealtype = 'PO' THEN d.facevalue
END
AS v_pofv,
CASE
WHEN d.dealtype = 'SO' THEN d.facevalue -- IF conditions converted to CASE
END
AS v_sofv
FROM mmdeal0_nwap d
INNER JOIN mmstock1 s ON d.securitycd = s.securitycd
WHERE d.dealdt >= p_fdt AND d.dealno = p_dealno AND d.facevalue > 0 AND d.dealtype IN (
'PO',
'SO'
) AND d.selforconstituent = 'S' AND d.securitytype = 'DGS'
) i
WHERE i.ownst > 0 --outer IF condition
)
ON (
t.dealno = s.dno AND t.dealdt = s.ddate AND t.securitycd = s.scd AND t.securitytype = s.stype AND
t.selforconstituent = s.sforco
) --where clause from your update
WHEN MATCHED THEN UPDATE SET t.wap1 = s.new_wp;
Please note that the code is untested, so you may have to fix some syntactic errors / editing mistakes which I may have made. But, I believe I have given you fair enough idea how to proceed.
Some general suggestions for investigating PL/SQL performance:
Add dbms_output messages containing counts and timings (or write the details to a log table). This may be tricky if the code never completes or produces too much output, but you may be able to create some simplified test data to limit the run.
Step through the code in the debugger until you can identify the issue, for example if some value is not being reset as expected or a loop is not being exited. It will also give you a feel for how long each step takes. (Desktop tools have different ways to launch the debugger, but they should all provide the feature.)
Use dbms_profiler to get a report on how many times each statement is called and much time is spent on it. Again you may need to restrict the run with simplified test data. Profiling is built into some desktop tools such as PL/SQL Developer so you can just click a button, or else it's straightforward to use on the command line. (There is also dbms_hprof, although it's more complicated to use for little benefit, and dbms_trace, although in my experience this doesn't tell you anything useful.)
You can tell a lot about what it is doing by querying v$session from another session. Some desktop tools have session monitors built in to make this easier.
If you have licenced the Diagnostics and Tuning Pack, you can get a lot of useful run-time information out of v$active_session_history.
You can monitor running or recently completed SQL with SQL Monitor. If you have access to it, Oracle Enterprise Manager / Cloud Control gives you a lot of this via a convenient browser based dashboard. I would also take each SQL statement and test it on the command line to make sure it did what I expected and completed in a reasonable time.
I am trying to create a script that will allow the user to select which CASE population to use from an ACCEPT when gathering student contact info.
PROMPT 'Select a popluation for emails'
PROMPT '1. Currently registered'
PROMPT '2. New Applicants'
PROMPT
ACCEPT cnt number PROMPT 'Selection: ';
...
CURSOR stu_lst IS
CASE &cnt
WHEN 1 THEN -- Current registered students.
select distinct SFRSTCA_PIDM pidm
from SFRSTCA
where SFRSTCA_TERM_CODE = '201403' and
SFRSTCA_LEVL_CODE = '01' and
SFRSTCA_RSTS_CODE = 'RE';
WHEN 2 THEN -- New applicants
select app_pidm pidm
from app
where app_term = 'Fall 2014';
ELSE
-- Incorrect selection.
DBMS_OUTPUT.PUT_LINE('Incorrect selection made.');
exit;
END;
END;
Assuming the two queries return the same data type, you could use a union with a filter that checks the variable in each part; something like:
DECLARE
CURSOR stu_lst IS
-- Current registered students.
select distinct SFRSTCA_PIDM pidm
from SFRSTCA
where &cnt = 1 and
SFRSTCA_TERM_CODE = '201403' and
SFRSTCA_LEVL_CODE = '01' and
SFRSTCA_RSTS_CODE = 'RE';
UNION ALL
-- New applicants
select app_pidm
from app
where &cnt = 2 and
app_term = 'Fall 2014';
invalid_argument EXCEPTION;
...
BEGIN
IF &cnt NOT IN (1, 2) THEN
RAISE invalid_argument;
END IF
FOR rec IN stu_lst LOOP
h_pidm := rec.pidm;
...
END LOOP;
EXCEPTION
WHEN invalid_argument THEN
dbms_output.put_line('Incorrect selection made.');
END;
/
You could also declare a cursor variable and open that with the appropriate query, inside a case statement within the main body of the block. This sticks with your explicit cursor syntax though.
Consider following situation: I have PL/pgSQL function which checks, If given auditor has some prerequisites for QS Auditor function. Thresholds of this prerequisites are defined in separate table quasar_settings. Every time, If is the function called, is executed SELECT which retrieves these prerequisites. This is quite inefficient, because this SELECT is called for every row. This quasar_settings table contains only one row. Is there any other more effective solution (global variable, caching, etc)?
Table quasar_settings has only one row.
Using PostgreSQL 9.3
PL/pgSQL function
CREATE OR REPLACE FUNCTION qs_auditor_training_auditing(auditor quasar_auditor) RETURNS boolean AS $$
DECLARE
settings quasar_settings%ROWTYPE;
BEGIN
SELECT s INTO settings
FROM quasar_settings s LIMIT 1;
RETURN auditor.nb1023_procedures_hours >= settings.qs_auditor_nb1023_procedures AND
-- MD Training
auditor.mdd_hours + auditor.ivd_hours >= settings.qs_auditor_md_training AND
-- ISO 9001 Trainig
(
auditor.is_aproved_for_iso13485 OR
(auditor.is_aproved_for_iso9001 AND auditor.iso13485_hours >= settings.qs_auditor_iso13485_training) OR
(auditor.iso13485_hours + auditor.iso9001_hours >= settings.qs_auditor_class_room_training)
);
END;
$$ LANGUAGE plpgsql;
Example of usage:
SELECT auditor.id, qs_auditor_training_auditing(auditor) FROM quasar_auditor auditor;
Do a cross join to the settings table in instead of calling the function at every row
select
a.id,
a.nb1023_procedures_hours >= s.qs_auditor_nb1023_procedures and
-- md training
a.mdd_hours + a.ivd_hours >= s.qs_auditor_md_training and
-- iso 9001 trainig
(
a.is_aproved_for_iso13485 or
(
a.is_aproved_for_iso9001 and
a.iso13485_hours >= s.qs_auditor_iso13485_training
) or
(a.iso13485_hours + a.iso9001_hours >= s.qs_auditor_class_room_training)
)
from
quasar_auditor a
cross join
quasar_settings s
I am working on Sql Developper an I created the following procedure in a package:
PROCEDURE VALIDER(a_session IN NUMBER) AS
i NUMBER;
TYPE type_tab IS TABLE OF PANIER%ROWTYPE;
tabSeances type_tab;
BEGIN
SELECT * BULK COLLECT INTO tabSeances
FROM PANIER
WHERE a_session = sessionweb;
i:=0;
FOR i IN 1 .. tabSeances.count LOOP
-- UPADTE DU NOMBRE DE PLACES LIBRES
BEGIN
UPDATE PROJECTION
SET remaining_seats = (remaining_seats - tabseances(i).nbrplaces)
WHERE num_copy = tabseances(i).num_copy
AND day = tabseances(i).dateseance
AND time_slot = tabseances(i).time_slot
AND movie = tabseances(i).movie;
COMMIT;
--UPDATE ON PANIER
UPDATE PANIER
SET valide = 1
WHERE sessionweb = a_session
AND num_copy = tabseances(i).num_copy
AND dateseance = tabseances(i).dateseance
AND time_slot = tabseances(i).time_slot
AND movie = tabseances(i).movie;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN raise_application_error(-20035, 'Pas de données');
WHEN OTHERS THEN raise_application_error(-20006,'Autres Erreurs');
END;
END LOOP;
END VALIDER;
The procedure executes normaly and I don't get an error.
I have a kind of product cart: "PANIER". I loop all the entries in thsi cart for one person (session) to validate them in the database and decrement the total number of seats.
But the field "remaining-seats" (from PROJECTIONS) in the first update don't work. The field isn't updated. I have already tried with other values but nothing.
I am sure that the procedure is executetd because the second update still works. It marks the cart entry as "CONFIRMED".
I don't have any trigger on this field.
My tables contains valid data (<>NULL).
I execute this procedure like this (in a BEGIN END; block):
CMDPLACES.VALIDER(1);
Thank for your reply.
Is it day or dateseance in your first update?
UPDATE PROJECTION
SET remaining_seats = (remaining_seats - tabseances(i).nbrplaces)
WHERE num_copy = tabseances(i).num_copy
AND dateseance = tabseances(i).dateseance
AND time_slot = tabseances(i).time_slot
AND movie = tabseances(i).movie;
Also as #ThorstenKettner was mentioning, the timestamp data in the date , may fail while comparing, so we have TRUNCATE the timestamp data using TRUNC() [if needed]!
If the date column is indexed, beware the index will not be used by the database .
To handle NO Data in UPDATE, you can check (SQL%ROWCOUNT > 0) to identify the number of rows updated!
Your first update compares days. What data type are these? In case you deal with DATETIME, make sure to compare without the time part if any. Use TRUNC to achieve this.
AND TRUNC(day) = TRUNC(tabseances(i).dateseance)