Creating a Procedure with Oracle - sql

im trying to get a job (ORACLE SHEDULER) done in oracle every week.
The point is to loop through the 'Historico' table every week, and check every 'ID_Academista' that is linked to the
'Academista' Table. After that it should compare 'Valor_Atual' to see if its > 20 (it comes from the table 'Tecnologia')
If so, it decrements by 1, if not, it stays the same.
To put it in better english: Every 'Tecnologia' Is a technology, each technology has a individual grade(Valor_Atual)
That will be inside the 'Academista'(student)
Table Historico
Database
Here is the code :
CREATE OR REPLACE PROCEDURE notas
IS
contar INTEGER := 0;
contar2 INTEGER;
BEGIN
SELECT COUNT(*) INTO contar2 FROM Academista;
WHILE contar <= contar2
LOOP
SELECT h.Valor_Atual, h.Valor_Antigo, h.Id_academista FROM HISTORICO h
WHERE h.Id_academista = contar ;
IF
h.Valor_Atual > 20
THEN
UPDATE HISTORICO
SET Valor_Atual = Valor_Atual + 1
WHERE h.Id_academista = contar;
ELSE
UPDATE HISTORICO
SET Valor_Atual = Valor_Atual - 1
WHERE h.Id_academista = contar ;
SET contar := contar + 1;
END IF;
END LOOP;
END;
Errors

You can't use selects inside a pl/sql block without a variable.
CREATE OR REPLACE PROCEDURE notas
IS
contar INTEGER := 0;
contar2 INTEGER;
h_var HISTORICO%rowtype;
BEGIN
SELECT COUNT(*) INTO contar2 FROM Academista;
WHILE contar <= contar2
LOOP
SELECT h.* into h_var FROM HISTORICO h
WHERE h.Id_academista = contar ;
IF
h_var.Valor_Atual > 20
THEN
UPDATE HISTORICO
SET Valor_Atual = Valor_Atual + 1
WHERE h_var.Id_academista = contar;
ELSE
UPDATE HISTORICO
SET Valor_Atual = Valor_Atual - 1
WHERE h_var.Id_academista = contar ;
SET contar := contar + 1;
END IF;
END LOOP;
END;

Related

How to browse an query id not follow PostgreSQL

I need your help about an modification of my actual code. Here it is :
\timing on
DO $$
BEGIN
DECLARE
i INT := 1; -- valeur de départ à changer pour la range d'id
j INT := i + 1;
numberUsers INT; -- Changer valeur numberUsers pour fin de la range d'id
commonSkills INT;
random INT;
idSphere INT := 2;
BEGIN
SELECT COUNT(*) INTO numberUsers FROM "WU_Users"; -- si numberUSers est modifié, commentez la ligne
WHILE i < numberUsers LOOP
WHILE j <= numberUsers LOOP
IF i !=j THEN
SELECT floor(random() * 10 + 35)::INT INTO random;
INSERT INTO "WU_MatchingUsers"("IDWU_User1", "IDWU_User2", "MatchingScore")
VALUES(i, j, random)
ON CONFLICT ("id")
DO
UPDATE SET "MatchingScore" = EXCLUDED."MatchingScore";
ELSE
RAISE NOTICE 'Same Users';
END IF;
j := j + 1;
END LOOP;
j := i + 1;
RAISE NOTICE 'i: %', i;
i := i + 1;
END LOOP;
END;
END $$;
\timing off
So this code take two id, and with two loops "browse" from i to numberUsers and insert into another table a random matching score, now I need to add this :
SELECT "IDWU_Users_WU_Users" FROM "WU_UsersSpheres" WHERE "IDWU_UsersSpheres" = idSphere;
This line return me a list of id from another table that I need to match, like the script above I match in this order :
1/2
1/3
etc etc
But now this new line can return me id like that :
532
1426
3236
84697
And I need to make the same matching system than before so like that :
532/1426
532/3236
532/84697
Kind Regards !

How to fix LOOP error PostgreSQL while trying to Select

Here is the code I'm Testing :
DO $$
DECLARE
i INT := 0;
BEGIN
WHILE i <= numberUsers
BEGIN
SELECT COUNT(*) FROM "Users";
SET i := i + 1;
END;
END $$;
But when I try to execute it it return me this error :
ERREUR: « LOOP » manquant à la fin de l'expression SQL
LIGNE 7 : SELECT COUNT(*) FROM "Users";
Kind Regards !
You have considerable structural errors in your code block:
DO $$
DECLARE
i INT := 0;
BEGIN
WHILE i <= numberUsers --<<< numberUsers is undefined and uninitialized
BEGIN --<<< this starts a nested block should be LOOP
SELECT COUNT(*) FROM "Users"; --<<< in a block you must tell plpgsql what to do with the result
SET i := i + 1; --<<< assignment does not use SET in plpgsql
END; --<<< terminated nested block, should terminate loop
END $$;
Correcting all the points from the block becomes:
do $$
declare
i int := 0;
numberusers int;
begin
select count(*)
into numberusers
from "users";
while i <= numberusers
loop
i := i + 1;
end loop;
end $$;
Per:
https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-CONTROL-STRUCTURES-LOOPS
WHILE boolean-expression LOOP
statements
END LOOP [ label ];
You need the LOOP END ... LOOP portions of the statement.
The error message tells you that something is missing from the expression, which is a clear indication that you have a syntax error. Try this code:
DO $$
DECLARE
i INT := 0;
BEGIN
WHILE i <= numberUsers LOOP
BEGIN
SELECT COUNT(*) FROM "Users";
SET i := i + 1;
END LOOP;
END $$;
Read more here: https://www.postgresqltutorial.com/plpgsql-while-loop/
DO $$
BEGIN
DECLARE
i INT := 0;
BEGIN
WHILE i <= numberUsers LOOP
SELECT COUNT(*) FROM "WU_Users";
i := i + 1;
END LOOP;
END $$;
But it's returning me this now :
psql:matching.sql:12: ERREUR: label de fin « matching » spécifié pour un bloc sans label
LIGNE 12 : END matching $$;

Multiple FORALL in a orasql procedure

Im trying to both delete then update some data from a table inside a procedure. It is my understanding that both can't be done in the same FORALL statement and that I can do multiple FORALL using the same cursor. The problem is that the second FORALL does not seem to do anything. Is there something that im not getting? Here is the code :
cursor cPARTY is
SELECT /* +parallel(4) */ DISTINCT p.ID,
CASE WHEN sf.PARTYID is null THEN 'delete'
ELSE 'switch'
END AS action,
pa.SOURCESYSTEMLID as sources
FROM CV_CLAIMS_TRAVEL.EPUR_PARTY p
LEFT JOIN CV_CLAIMS_TRAVEL.STAR_FILE sf ON sf.PARTYID = p.ID
LEFT JOIN CV_CLAIMS_TRAVEL.PARTY pa ON pa.ID = p.ID;
type type_party is TABLE OF cPARTY%ROWTYPE INDEX BY PLS_INTEGER;
t_party type_party;
nLOT pls_integer := 1;
nbDeleted int :=0;
nbSwitched int := 0;
begin
p_tableName := 'PARTY(INDIVIDU)';
allBEGIN_TIMESTAMP := systimestamp;
open cPARTY;
loop
lotBEGIN_TIMESTAMP := systimestamp;
dbms_application_info.set_module('CV_CLAIMS_TRAVEL.PARTY',trim(to_char(nLOT * nLIMIT,'999G999G999')));
FETCH cPARTY BULK COLLECT
INTO t_party
limit nLIMIT;
exit when t_party.count = 0;
p_nbLinesTransfered :='Erreur: Suppression de données';
dbms_application_info.set_module('CV_CLAIMS_TRAVEL.PARTY - DELETING',trim(to_char(nLOT * nLIMIT,'999G999G999')));
FORALL i IN t_party.FIRST .. t_party.LAST
DELETE /*+parallel(4)*/ FROM CV_CLAIMS_TRAVEL.PARTY
WHERE ID = t_party(i).ID
AND t_party(i).action = 'delete';
nbDeleted := nbDeleted + sql%rowcount;
FORALL i IN t_party.FIRST .. t_party.LAST
UPDATE CV_CLAIMS_TRAVEL.PARTY
SET UPDATEDATE = sysdate, SOURCESYSTEMLID = 'SOURCE_SYSTEM:0000000000', UPDATEDBYUSERID = 'CV_CLAIM_INTEG'
WHERE ID = t_party(i).ID
AND t_party(i).sources='SOURCE_SYSTEM:0000000004'
AND t_party(i).action = 'switch';
nbSwitched := nbSwitched + sql%rowcount;
commit;
lotEND_TIMESTAMP :=systimestamp;
trc.trc_message('lotELAPSED '||to_char(nLOT * nLIMIT,'999G999G990')||' Rows. ELAPSED '|| replace(substr(to_char(lotEND_TIMESTAMP - lotBEGIN_TIMESTAMP, 'HH24:MI:SS.FF3'),1,20),'+000000 ','+'));
nLOT := nLOT + 1;
end loop;
close cPARTY;
commit;
gather_stats('CV_CLAIMS_TRAVEL', 'PARTY');

PL/SQL exception not reached, function doesn't return

I have a PL/SQL function and it doesn't seem to reach an exception when I encounter no_data_found. I have looked through and tried to add my select statement into its own begin block but I alway get syntax errors. Where do I need to put the exception ? Thank you
create or replace FUNCTION is_artikl_eligible_for_zamjena (
--input vars
inSifraArtikla IN A_ZAMJENA_ARTIKLI.SIFRA_ARTIKLA%type,
inDatumPocetak IN VARCHAR2,
inSkladiste IN A_ZAMJENA.SKL%type,
inProdavaonice IN A_ZAMJENA.POPIS_PROD%type
)
RETURN NUMBER
is
existingSifraArtikla A_ZAMJENA_ARTIKLI.SIFRA_ARTIKLA%type;
existingBrojZamjene A_ZAMJENA.BROJ_ZAMJENE%type;
existingDatumKraj A_ZAMJENA.DATUM_KRAJ%type;
existingSkladiste A_ZAMJENA.SKL%type;
existingProdavaonice A_ZAMJENA.POPIS_PROD%type;
BEGIN
dbms_output.enable();
--check if there is sifra_artikla in a_zamjena_artikli where a_zamjena.datumKraj != null or a_zamjena.datumKraj > sysdate
for i IN (
SELECT azam.BROJ_ZAMJENE, azam.DATUM_KRAJ, azam.SKL, azam.POPIS_PROD, azamAr.SIFRA_ARTIKLA
FROM A_ZAMJENA azam JOIN A_ZAMJENA_ARTIKLI azamAr
ON azam.BROJ_ZAMJENE = azamAr.BROJ_ZAMJENE
WHERE azamAr.SIFRA_ARTIKLA = inSifraArtikla
)
LOOP
existingBrojZamjene :=i.BROJ_ZAMJENE;
--existingDatumKraj := TO_CHAR(COALESCE(i.DATUM_KRAJ,'21-01-25 00:00'));
existingDatumKraj := i.DATUM_KRAJ;
existingSkladiste := COALESCE(i.SKL, '0');
existingProdavaonice := COALESCE(i.POPIS_PROD, 0);
existingSifraArtikla := i.SIFRA_ARTIKLA;
if existingDatumKraj IS NOT NULL AND existingDatumKraj < (sysdate -1) then --level 0
dbms_output.put_line('Datum kraj postojeće zamjene nije null ali je završio');
return 1;
else
if (existingDatumKraj IS NULL) OR (existingDatumKraj > sysdate) then --level 1
dbms_output.put_line('Zamjene imaju preklapajuće datume, provjeravam da li su na istim skladištima ili prodavaonicama');
if existingSkladiste != inSkladiste OR existingProdavaonice != inProdavaonice then -- level 2
dbms_output.put_line('Zamjene imaju preklapajuće datume ali nisu na istim skladištima ili prodavaonicama');
return 1;
else -- level 2
return 0;
end if; --level 2
else --else za datum kraj, level 1
dbms_output.put_line('Zamjene nemaju preklapajuće datume');
return 1;
end if; --level 1
end if; --level 0
END LOOP;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('nema retka');
RETURN 1;
END;
Untested, but I would probably use a local variable to check whether my cursor loop found any rows, and check that at the end. (It could be a Boolean or a counter or something else. I've used a counter below.)
create or replace function is_artikl_eligible_for_zamjena
( insifraartikla in a_zamjena_artikli.sifra_artikla%type
, indatumpocetak in varchar2
, inskladiste in a_zamjena.skl%type
, inprodavaonice in a_zamjena.popis_prod%type )
return number
is
rows_found pls_integer := 0;
begin
dbms_output.enable();
-- check if there is sifra_artikla in a_zamjena_artikli where a_zamjena.datumKraj != null or a_zamjena.datumKraj > sysdate
for i in (
select azam.broj_zamjene
, azam.datum_kraj
, coalesce(azam.skl,'0') as skl
, coalesce(azam.popis_prod,0) as popis_prod
, azamar.sifra_artikla
from a_zamjena azam
join a_zamjena_artikli azamar
on azamar.broj_zamjene = azam.broj_zamjene
where azamar.sifra_artikla = insifraartikla
)
loop
rows_found := rows_found +1;
if i.datum_kraj < sysdate - 1 then
--level 0
dbms_output.put_line('Datum kraj postojeće zamjene nije null ali je završio');
return 1;
else
if i.datum_kraj is null or i.datum_kraj > sysdate then
--level 1
dbms_output.put_line('Zamjene imaju preklapajuće datume, provjeravam da li su na istim skladištima ili prodavaonicama');
if i.skl != inskladiste or i.popis_prod != inprodavaonice then
-- level 2
dbms_output.put_line('Zamjene imaju preklapajuće datume ali nisu na istim skladištima ili prodavaonicama');
return 1;
else
-- level 2
return 0;
end if; --level 2
else
--else za datum kraj, level 1
dbms_output.put_line('Zamjene nemaju preklapajuće datume');
return 1;
end if; --level 1
end if; --level 0
end loop;
if rows_found = 0 then
raise no_data_found;
end;
exception
when no_data_found then
dbms_output.put_line('nema retka');
return 1;
end;
I'm not sure raising a no_data_found exception is a particularly good approach here, though. In fact I'm not keen on a return inside a loop either. Maybe the problem goes away if you just assign the value you want to return to a local variable during the loop, and check it at the end, handling the case where it has no value.
With a local variable and a single return point instead of manipulating exception handlers, it would look something like this:
create or replace function is_artikl_eligible_for_zamjena
( insifraartikla in a_zamjena_artikli.sifra_artikla%type
, indatumpocetak in varchar2
, inskladiste in a_zamjena.skl%type
, inprodavaonice in a_zamjena.popis_prod%type )
return number
is
l_result number;
begin
dbms_output.enable();
-- check if there is sifra_artikla in a_zamjena_artikli where a_zamjena.datumKraj != null or a_zamjena.datumKraj > sysdate
for i in (
select azam.broj_zamjene
, azam.datum_kraj
, coalesce(azam.skl,'0') as skl
, coalesce(azam.popis_prod,0) as popis_prod
, azamar.sifra_artikla
from a_zamjena azam
join a_zamjena_artikli azamar
on azam.broj_zamjene = azamar.broj_zamjene
where azamar.sifra_artikla = insifraartikla
)
loop
if i.datum_kraj < sysdate - 1 then
--level 0
dbms_output.put_line('Datum kraj postojeće zamjene nije null ali je završio');
l_result := 1;
else
if i.datum_kraj is null or i.datum_kraj > sysdate then
--level 1
dbms_output.put_line('Zamjene imaju preklapajuće datume, provjeravam da li su na istim skladištima ili prodavaonicama');
if i.skl != inskladiste or i.popis_prod != inprodavaonice then
-- level 2
dbms_output.put_line('Zamjene imaju preklapajuće datume ali nisu na istim skladištima ili prodavaonicama');
l_result := 1;
else
-- level 2
l_result := 0;
end if; --level 2
else
--else za datum kraj, level 1
dbms_output.put_line('Zamjene nemaju preklapajuće datume');
l_result := 1;
end if; --level 1
end if; --level 0
end loop;
if l_result is then
dbms_output.put_line('nema retka');
l_result := 1;
end;
return l_result;
end;
I'm not sure what the dbms_output.put_line calls are doing, but that's another question.
You simply cannot raise a NO_DATA_FOUND exception using a for loop. You can do using a goto statement for insatance. See below example.
DECLARE
v_attr char(88);
CURSOR SELECT_USERS IS
SELECT id
FROM USER_TABLE
WHERE USERTYPE = 'X';
BEGIN
FOR user_rec IN SELECT_USERS
LOOP
BEGIN
SELECT attr
INTO v_attr
FROM ATTRIBUTE_TABLE
WHERE user_id = user_rec.id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- user does not have attribute, continue loop to next record.
goto end_loop; ---Adding a label
END;
<<end_loop>> --Label
null;
END LOOP;
END;

PLSQL Printing prime numbers

I want to print prime numbers between 1 to 50. But I don't understand what I am doing wrong in my code. After BEGIN, SQLDeveloper says I had an error because it expected another sign and not = .
SET SERVEROUTPUT ON
DECLARE
i NUMBER;
counter NUMBER;
n NUMBER;
k NUMBER;
BEGIN
i := 2;
counter := 0;
n := 50;
k := n/2;
FOR i IN 1..k LOOP
IF (n%i := 0 ) THEN
counter := 1;
END IF;
IF (counter := 0) THEN
DBMS_OUTPUT.PUT_LINE(n||' is prime number');
END IF;
END LOOP;
END;
SET SERVEROUTPUT ON
DECLARE
i NUMBER;
counter NUMBER;
n NUMBER;
k NUMBER;
BEGIN
i := 2;
counter := 0;
n := 50;
k := floor(n/2);
FOR i IN 1..k LOOP
IF (mod(n, i) = 0 ) THEN
counter := 1;
END IF;
IF (counter = 0) THEN
DBMS_OUTPUT.PUT_LINE(n||' is prime number');
END IF;
END LOOP;
END;
k := n/2; -- added FLOOR (k is NUMBER, by default it's NUMBER(38, max_scale))
IF (n%i := 0 ) THEN -> IF (mod(n, i) = 0 ) THEN
Oracle has MOD function for remainder + to compare you need to use =,
:= is for assignment.
DECLARE
counter NUMBER;
k NUMBER;
BEGIN
FOR n IN 1..50 LOOP
counter := 0;
k := floor(n/2);
FOR i IN 2..k LOOP
IF (mod(n, i) = 0 ) THEN
counter := 1;
END IF;
END LOOP;
IF (counter = 0) THEN
DBMS_OUTPUT.PUT_LINE(n||' is prime number');
END IF;
END LOOP;
END;
In your IF clause you are assigning the value instead of comparison. You are using := operator, where you shd be using =
It shud be like (IF counter = 0) then
......
Also I dont think n%i would work, you could do IF (trunc(n) = n) then ... or IF (mod (n,i) =0) then ...
--this function is check prime number.
create or replace function prime_a(x number) return
varchar2 is
n integer;
ans varchar2(50);
begin
n:=(x/2);
for i in 2..n loop
if mod(x,i)=0
then ans:='not a prime';
exit;
else ans:='prime';
end if;
end loop;
return ans;
end;
/
step-1:
create table tob(prime number);
step-2:
create or replace procedure ro(m number,n number)
is
a integer;
co Boolean;
begin
for j in m..n loop
co:=false;
co:=(j=1 );
a:=(j/2);
for i in 2..a loop
co:=(mod(j,i)=0);
exit when co;
end loop;
if(not co) then
insert into tob values(J);
end if;
end loop;
commit;
end;
/
step-3:
exec ro(1,50);
step-4: check:-
select * from tob;
You should create or replace in your source code:
function prime_a(x number) return
varchar2 is
n integer;
ans varchar2(50);
begin
n:=(x/2);
for i in 2..n loop
if mod(x,i)=0
then ans:='not a prime';
exit;
else ans:='prime';
end if;
end loop;
return ans;
end;
/
Why don't you just check for previous prime divisibility?
create table prime (primeno bigint)
declare #counter bigint
set #counter = 2
while #counter < 1000000
begin
if not exists(select top 1 primeno from prime where #counter % primeno = 0)
insert into prime select #counter
set #counter = #counter + 1
end
select * from prime order by 1
You could certainly cap the numbers you are checking against in the where condition to reduce your overheads further.
declare
i number;
j number;
k number:=0;
begin
for i in 1..50
loop
for j in 2..i-1
loop
if mod(i,j)=0 then
k:=1;
exit;
end if;
end loop;
if k=0 then
dbms_output.put_line(i);
end if;
k:=0;
end loop;
end;
/
create or replace function prime_a(x number) return varchar2 is
n integer;
ans varchar2(50);
begin
n:=(x/2);
for i in 2..n loop
if mod(x,i)=0 then
ans:='not a prime';
exit;
else
ans:='prime';
end if;
end loop;
return ans;
end;
/