Oracle SQL Create Trigger to insert SEQ.nextval - oracle

I am creating a trigger to add same SEQ.nextval numbers to two fields such as follows:
SITE_NUM SITE_NUM_COPY Site_Name
346 346 XYZ
347 347 ABC
348 348 DEF
Whenever a new row is added through an application I want the SITE_NUM and SITE_NUM_COPY will be auto generated through the trigger.
This is what I have put together:
create or replace trigger SITE_TRIGGER
before insert or update on STRATEGY_SITES for each row
begin
if updating then
if :new.SITE_NUM is null and :new.SITE_NUM_Copy is NULL then
:new.SITE_NUM := :old.SITE_NUM
and :old.SITE_NUM := :new.SITE_NUM_COPY;
end if;
end if;
if inserting then
:new.SITE_NUM := SITENUM_SEQ.nextval
and :new.SITE_NUM_COPY := SITENUM_SEQ.nextval;
end if;
end;
Getting the following error:
Error(7,31): PLS-00103: Encountered the symbol "=" when expecting one of the following:
. ( * # % & = - + ; < / > at in is mod remainder not rem <> or != or ~= >= <= <> and or like like2 like4 likec between || indicator multiset member submultiset
The symbol "* was inserted before "=" to continue.

The error is not very helpful but is stemming from you using and to, apparently, seperate two statements inside each if block. The statement terminator/separator is a semicolon.
It looks like you are trying to do this for the first one:
if updating then
if :new.SITE_NUM is null and :new.SITE_NUM_Copy is NULL then
:new.SITE_NUM := :old.SITE_NUM;
:old.SITE_NUM := :new.SITE_NUM_COPY; -- illegal
end if;
end if;
but you cannot change the :old values; it isn't clear what you expect to happen, ot least as the value assigning must be null. You may just want to remove that line.
And for the second part:
if inserting then
:new.SITE_NUM := SITENUM_SEQ.nextval;
:new.SITE_NUM_COPY := SITENUM_SEQ.currval;
end if;
I've changed the second assignment to use currval because you said you wanted the same value for both columns. If you call nextval for both assignments they will get different values simce the sequnce will increment twice.

Related

Write a PL/SQL block to insert numbers into the MESSAGES table. Insert the numbers 1 through 10, excluding 6 and 8

I'm trying to figure out how to exclude these numbers(6 and 8) when the loop happens. Also, for this question, I CAN'T use FOR and WHILE loops. The question states to ONLY use a basic loop since the lessons after will teach me how to use it. Also, does anyone know if I'm allowed to insert multiple END LOOPs? It's also possible that this syntax may not be legal.
EDIT: I'm pretty sure I've tried doing IF v_results >10 THEN EXIT; but the same error message occurred.
DECLARE
v_results messages.results%TYPE := 0 ; --data type is NUMBER
BEGIN
LOOP
SELECT results INTO v_results
FROM messages;
v_results := v_results + 1; --to increment
IF v_results = ANY(6,8)
THEN
END LOOP; --i thought maybe if I added this, the loop can start over
ELSE
INSERT INTO MESSAGES(results)
VALUES (v_results);
EXIT WHEN v_results >10;
END IF;
END LOOP;
END;
The error that I am getting.
ORA-06550: line 15, column 9:
PLS-00103: Encountered the symbol "END" when expecting one of the following:
( begin case declare exit for goto if loop mod null pragma
raise return select update while with
<<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
You could just avoid the 6 and 8 by adding 1 into it using the IF statement. There is no need for ELSE statement.
Also, you will need to use MAX function while fetching data from the MESSAGE table so that it can return the only max number. Without MAX function, you will get an error of -- multiple rows returned.
END LOOP must be associated with a single LOOP statement. so you can not write two END LOOP when there is a single LOOP statement.
DECLARE
V_RESULTS MESSAGES.RESULTS%TYPE := 0; --data type is NUMBER
BEGIN
LOOP
SELECT
MAX(RESULTS) -- used max to find ONLY ONE MAX RECORD
INTO V_RESULTS
FROM
MESSAGES;
V_RESULTS := NVL(V_RESULTS, 0) + 1; --to increment
IF V_RESULTS IN ( -- used in here
6,
8
) THEN
V_RESULTS := V_RESULTS + 1;
END IF;
INSERT INTO MESSAGES ( RESULTS ) VALUES ( V_RESULTS );
EXIT WHEN V_RESULTS >= 10;
END LOOP;
END;
db<>fiddle demo
Cheers!!
declare
i number:=1;
begin
loop
if i not in(6,8) then
insert into msg values(i);
end if;
i :=i+1;
exit when i>10;
end loop;
end;

My Trigger getting looping and Case Error when i try to join two tables on it

I have 2 tables, Contract and Bankslip.
I need to get the date field from the Contract table, and set the date on Bankslip table, but it's getting in a loop, I think!
How can i do it?
Here is my code:
create or replace TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
BEFORE INSERT ON GFLANCAM
FOR EACH ROW
DECLARE
DATA_INICIO_CONTRATO DATE;
BEGIN
CASE WHEN :NEW.DOCUMENTO <> ' ' then
SELECT dt_inicio
INTO DATA_INICIO_CONTRATO
from ctcontra
where cd_contrato = :NEW.documento;
:NEW.data := DATA_INICIO_CONTRATO;
END CASE;
END;
What am I doing wrong?
Much of the trigger is unnecessary.
You can accomplish your goal without the CASE and without defining a variable.
CREATE OR REPLACE TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
BEFORE INSERT
ON GFLANCAM
FOR EACH ROW
BEGIN
-- Consider following:
-- IF NVL (:NEW.DOCUMENTO, ' ') <> ' '
IF :NEW.DOCUMENTO <> ' '
THEN
-- Following line may cause ORA-01403: no data found
SELECT dt_inicio INTO :NEW.data FROM ctcontra WHERE cd_contrato = :NEW.documento;
END IF;
END;
/
A few notes:
If you want to catch NULL values then add the NVL shown above.
Watch out for the case where a corresponding record is not found in ctcontra--this condition would result in ORA-01403: no data found (which might be exactly what you want in this case).
Make sure that ctcontra has only one record for each cd_contrato value, otherwise you will get a ORA-01422: exact fetch returns more than requested number of rows.
Take a look at the update:
{CREATE OR REPLACE TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
AFTER INSERT ON GFLANCAM
FOR EACH ROW
DECLARE
DATA_INICIO_CONTRATO DATE;
BEGIN
IF DOCUMENTO <> ' ' THEN
SELECT dt_inicio INTO DATA_INICIO_CONTRATO from ctcontra where cd_contrato =
DOCUMENTO;
UPDATE GFLANCAM SET DATA = DATA_INICIO_CONTRATO;
END IF;
END;}

Why I receive "NO DATA FOUND" exception when I use RETURNING clause after update statement?

I'm coding a simple LOOP FOR to update a column from a table using information populated in an associative array. All seems right when I only use UPDATE statement but when I add a RETURNING clause I receive "NO DATA FOUND" error. Thanks!
DECLARE
TYPE emps_info IS TABLE OF employees23%ROWTYPE
INDEX BY PLS_INTEGER;
t_emps_current_info emps_info;
t_emps_new_info emps_info;
BEGIN
SELECT *
BULK COLLECT INTO t_emps_current_info
FROM employees;
FOR emps_index IN t_emps_current_info.FIRST .. t_emps_current_info.LAST
LOOP
IF
NVL(t_emps_current_info(emps_index).commission_pct, 0) = 0 THEN
UPDATE employees23
SET commission_pct = 0.3
WHERE employee_id = t_emps_current_info(emps_index).employee_id;
ELSIF
t_emps_current_info(emps_index).commission_pct BETWEEN 0.1 AND 0.3 THEN
UPDATE employees23
SET commission_pct = 0.5
WHERE employee_id = t_emps_current_info(emps_index).employee_id;
END IF;
END LOOP;
END;
Now When I add RETURNING clause I receive following error:
DECLARE
TYPE emps_info IS TABLE OF employees23%ROWTYPE
INDEX BY PLS_INTEGER;
t_emps_current_info emps_info;
t_emps_new_info emps_info;
BEGIN
SELECT *
BULK COLLECT INTO t_emps_current_info
FROM employees;
FOR emps_index IN t_emps_current_info.FIRST .. t_emps_current_info.LAST
LOOP
IF
NVL(t_emps_current_info(emps_index).commission_pct, 0) = 0 THEN
UPDATE employees23
SET commission_pct = 0.3
WHERE employee_id = t_emps_current_info(emps_index).employee_id
RETURNING commission_pct
INTO t_emps_new_info(emps_index).commission_pct;
ELSIF
t_emps_current_info(emps_index).commission_pct BETWEEN 0.1 AND 0.3 THEN
UPDATE employees23
SET commission_pct = 0.5
WHERE employee_id = t_emps_current_info(emps_index).employee_id
RETURNING commission_pct
INTO t_emps_new_info(emps_index).commission_pct;
END IF;
DBMS_OUTPUT.PUT_LINE('EMPLOYEE_ID: ' ||
t_emps_current_info(emps_index).employee_id ||
' OLD COMMISSION: ' ||
NVL(t_emps_current_info(emps_index).commission_pct, 0)
|| ' NEW COMMISSION: ' ||
t_emps_new_info(emps_index).commission_pct);
END LOOP;
END;
Informe de error -
ORA-01403: no data found
ORA-06512: at line 22
01403. 00000 - "no data found"
*Cause: No data was found from the objects.
*Action: There was no data from the objects which may be due to end of fetch.
This part in your code
DBMS_OUTPUT.PUT_LINE('EMPLOYEE_ID: ' ||
t_emps_current_info(emps_index).employee_id ||
' OLD COMMISSION: ' ||
NVL(t_emps_current_info(emps_index).commission_pct, 0)
|| ' NEW COMMISSION: ' ||
t_emps_new_info(emps_index).commission_pct);
prints both the original array as well as the returning array .
At some point of time during your update it may happen that your update statement doesnt update any row , in that case your original array will have a value at that index , but your returning array wont
So check for the existence of index to avoid this error
case when t_emps_new_info.exists(emps_index) then
dbms_output.put_line('print something') ;
else dbms_output.put_line('print something else') ;
end case;
You are getting NO_DATA_FOUND because nowhere in your code are you initialising (creating) the t_emps_new_info(emps_index) records - whenever your code tries to assign t_emps_new_info(emps_index).commission_pct it will fail.
I'm not sure why it should be necessary, but I would change each UPDATE statement return the value into a simple local variable. Then I'd set the table record field to the simple variable.
Probably unrelated, but I noticed that your ELSIF test isn't doing an NVL() on the existing t_emps_current_info(emps_index).commission_pct, as your IF test does.
If it was me, I'd log the updates of both sides of the IF-ELSEIF to see if one update is working while the other fails. Maybe it's just failing on the ELSIF, because of the missing NVL()?

How to run Oracle in SQLFiddle

I am new to Oracle sql. I got a piece of code from the web, and paste it into sqlfiddle (http://sqlfiddle.com/):
For the schema, I created a temp table, which will be used in the sql query:
CREATE Global Temporary TABLE temp
(id number
,x number)
,y CHAR(50))
ON COMMIT DELETE ROWS;
I clicked build schema, which tells me "Schema Ready".
Then I paste the following query which is from Oracle official website on the right pane:
-- available online in file 'sample1'
DECLARE
x NUMBER := 100;
BEGIN
FOR i IN 1..10 LOOP
IF MOD(i,2) = 0 THEN -- i is even
INSERT INTO temp VALUES (i, x, 'i is even');
ELSE
INSERT INTO temp VALUES (i, x, 'i is odd');
END IF;
x := x + 100;
END LOOP;
COMMIT;
END;
When I press run sql, and it returns errors:
ORA-06550: line 3, column 18: PLS-00103: Encountered the symbol
"end-of-file" when expecting one of the following: * & = - + ; < / >
at in is mod remainder not rem <> or != or ~= >= <=
<> and or like like2 like4 likec between || multiset member
submultiset
You need to change default query terminator([;]) to sth else than ; and you may need to add this separator and new line between blocks of code:
SqlFiddleDemo
DECLARE
x NUMBER := 100;
BEGIN
FOR i IN 1..10 LOOP
IF MOD(i,2) = 0 THEN
INSERT INTO temp VALUES (i, x, 'i is even');
ELSE
INSERT INTO temp VALUES (i, x, 'i is odd');
END IF;
x := x + 100;
END LOOP;
COMMIT;
END;
What's up with that [ ; ] button under each panel?
This obscure little button determines how the queries in each of the panels get broken up before they are sent off to the database.
This button pops open a dropdown that lists different "query
terminators." Query terminators are used as a flag to indicate (when
present at the end of a line) that the current statement has ended.
The terminator does not get sent to the database; instead, it merely
idicates how I should parse the text before I execute the query.
Oftentimes, you won't need to touch this button; the main value this feature will have is in
defining stored procedures. This is because it is often the case that
within a stored procedure's body definition, you might want to end a
line with a semicolon (this is often the case). Since my default query
terminator is also a semicolon, there is no obvious way for me to see
that your stored procedure's semicolon isn't actually the end of the
query. Left with the semicolon terminator, I would break up your
procedure definition into incorrect parts, and errors would certainly
result. Changing the query terminator to something other than a
semicolon avoids this problem.

PLS-00103: Encountered the symbol "1" when expecting one of the following: (

Wrote the following PLSQL script to produce a report and am getting the error messages
Error at line 5
ORA-06550: line 61, column 18:
PLS-00103: Encountered the symbol "1" when expecting one of the following:
(
I've been through the code many times and I cannot find the error. Any help would be greatly appreciated.
I'm currently working in Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
SET serveroutput ON size 1000000;
DECLARE
TYPE TITLE_RECORD_TYPE IS RECORD
(id number(19),
gaid varchar2(20),
artist_legal_name varchar2(510),
artist_display_title varchar2(510),
display_title varchar2(510),
category varchar2(255),
type varchar2(255),
sub_type varchar2(255));
TITLE_RECORD TITLE_RECORD_TYPE;
v_title varchar2(510);
v_artist varchar2(510);
v_total_rows_error number(20) := 0;
v_row_count number(10) := 0;
v_error_desc varchar2(200) := NULL;
v_error_code number(19);
CURSOR ARTIST_TITLE_CURSOR is
select track_artist,track_title
from asset_artist_title;
CURSOR QUERY_CURSOR is
select distinct g1.gaid,g2.legal_name,g1.artist_display_title,
g1.display_title,g1.category,g1.type,g1.sub_type
from gcdm_app_rpt.rpt_asset g1,
gcdm_app_rpt.rpt_artist g2
where g1.artist_id = g2.id
and g1.is_deleted <> 'Y'
and g1.is_core = 'Y'
and g2.is_core = 'Y'
and g1.title like v_title||'%'
and g1.artist_display_title like v_artist||'%';
BEGIN
OPEN ARTIST_TITLE_CURSOR;
LOOP
FETCH ARTIST_TITLE_CURSOR into v_artist,v_title;
EXIT WHEN ARTIST_TITLE_CURSOR%NOTFOUND or ARTIST_TITLE_CURSOR%NOTFOUND IS NULL;
SELECT count(*)
INTO v_row_count
FROM gcdm_app_rpt.rpt_asset g1,
gcdm_app_rpt.rpt_artist g2
WHERE g1.artist_id = g2.id
AND g1.is_core = 'Y'
AND g1.is_deleted <> 'Y'
AND g2.is_core = 'Y'
AND g1.title like v_title||'%'
AND g1.artist_display_title like v_artist||'%';
IF v_row_count < 1 THEN
v_error_desc := 'Matching Asset record for '||v_artist||' - '||v_title||' not found';
DBMS_OUTPUT.PUT_LINE('Error: '||v_error_desc||'.');
v_row_count := 0;
v_total_rows_error := v_total_rows_error + 1;
ELSE
OPEN QUERY_CURSOR
FOR i in 1..ARTIST_TITLE_CURSOR
LOOP
FETCH QUERY_CURSOR into TITLE_RECORD;
EXIT WHEN QUERY_CURSOR%NOTFOUND or QUERY_CURSOR%NOTFOUND IS NULL;
DBMS_OUTPUT.PUT_LINE(title_record.id,title_record.gaid,title_record.artist_legal_name,title_record.artist_display_name,
title_record.display_title,title_record.category,title_record.type,title_record.sub_type);
END LOOP;
CLOSE QUERY_CURSOR;
v_row_count := 0;
END IF;
END LOOP;
CLOSE ARTIST_TITLE_CURSOR;
DBMS_OUTPUT.PUT_LINE(chr(0));
IF v_total_rows_error > 0 THEN
DBMS_OUTPUT.PUT_LINE('Total Rows in error: '||v_total_rows_error);
END IF;
DBMS_OUTPUT.PUT_LINE(CHR(0));
EXCEPTION
WHEN OTHERS THEN
v_error_desc := SQLERRM;
v_error_code := SQLCODE;
DBMS_OUTPUT.PUT_LINE('Error: '||v_error_desc||' - '||v_error_code);
END;
It's line 67 in what you've posted, not 61, but still; this line is not right:
FOR i in 1..ARTIST_TITLE_CURSOR
You're trying to loop over a range of numbers - perhaps you wanted the number of records returned by the cursor, which you can't get - but your end 'number' is a cursor, so not legal in that context.
But it seems to be completely out of place anyway as you're looping over the QUERY_CURSOR records, so I wouldn't think the ARTIST_TITLE_CURSOR is relevant at this point. And you aren't attempting to use i. It looks like you can just remove that line.
More importantly, the previous line is missing a semi-colon:
OPEN QUERY_CURSOR;
Because it doesn't have one it's seeing the FOR and expecting a cursor query.
Following up on comments about why you have that FOR 1..v_row_count, it's still a bit redundant. You're limiting the number of fetches you do to match the count you got previously, from essentially the same query as you have in the cursor, which means you don't quite ever hit the EXIT WHEN QUERYCURSOR%NOTFOUND condition - that would come from the v_row_count+1 loop iteration. Normally you wouldn't know how many rows you expect to see before you loop over a cursor.
You don't really need to know here. The count query is repetitive - you're querying the same data you then have to hit again for the cursor, and you have to maintain the query logic in two places. It would be simpler to forget the count step, and instead keep a counter as you loop over the cursor; then handle the zero-rows condition after the loop. For example:
DECLARE
...
BEGIN
OPEN ARTIST_TITLE_CURSOR;
LOOP
FETCH ARTIST_TITLE_CURSOR into v_artist,v_title;
EXIT WHEN ARTIST_TITLE_CURSOR%NOTFOUND;
-- initialise counter for each ARTIST_TITLE
v_row_count := 0;
OPEN QUERY_CURSOR;
LOOP
FETCH QUERY_CURSOR into TITLE_RECORD;
EXIT WHEN QUERY_CURSOR%NOTFOUND;
-- increment 'found' counter
v_row_count := v_row_count + 1;
DBMS_OUTPUT.PUT_LINE(title_record.id
||','|| title_record.gaid
||','|| title_record.artist_legal_name
||','|| title_record
||','|| artist_display_name
||','|| title_record.display_title
||','|| title_record.category
||','|| title_record.type
||','|| title_record.sub_type);
END LOOP;
CLOSE QUERY_CURSOR;
-- now check if we found anything in the QUERY_CURSOR loop
IF v_row_count < 1 THEN
v_error_desc := 'Matching Asset record for '||v_artist||' - '||v_title||' not found';
DBMS_OUTPUT.PUT_LINE('Error: Matching Asset record for '
|| v_artist || ' - ' || v_title || ' not found.');
v_total_rows_error := v_total_rows_error + 1;
END IF;
END LOOP;
CLOSE ARTIST_TITLE_CURSOR;
--DBMS_OUTPUT.PUT_LINE(chr(0));
-- presumably this was meant to put out a blank line; use this instead
DBMS_OUTPUT.NEW_LINE;
IF v_total_rows_error > 0 THEN
DBMS_OUTPUT.PUT_LINE('Total Rows in error: '||v_total_rows_error);
END IF;
--DBMS_OUTPUT.PUT_LINE(CHR(0));
DBMS_OUTPUT.NEW_LINE;
END;
I've also taken out the exception handler because it isn't really adding anything; you'd see the code and message without it, even if you didn't have server output on; and catching WHEN OTHERS is a bad habit to get into.
You also don't need to declare your record type. You could use an implicit cursor anyway and avoid the type and variable completely, but even with the cursor definition you have, you could put this afterwards instead:
TITLE_RECORD QUERY_CURSOR%ROWTYPE;
There are various ways to open and loop over cursors, and you're using one of the more explicit ones - which isn't a bad thing for learning about them, but be aware of the options too.

Resources