Dynamically checking if value is in sql query - plsql

I have a dynamically generated plsql region, in it, I have a bunch of dynamically generated items, some are popup_from_query and those have a query stored in an underlying table. But they also enable you to just type the return value in. So now I need to somehow check if the value you entered is in the LOV.
So I added some code to my validation(some also have extra conditions so I check those). I made a function that returns the relevant LOV, but reworked to something like this:
SELECT id --this is always the name of the column containing return value
FROM ( select something id, --this is the return value
something_else name --display value
from table
WHERE conditions)
WHERE id= :param;
This selects the return column from the lov, where the value is equal to the value we have entered, so this returns the entered value if it is in the LOV, and nothing if its not a valid value.
IF checking_lov THEN
a_lov := function_that_returns_an_sql_query;
DECLARE
x VARCHAR2(100);
BEGIN
EXECUTE IMMEDIATE
a_lov
INTO x
USING apex_application.g_f02(i); --value of parameter
EXCEPTION
WHEN no_data_found THEN
x := NULL;
END;
IF x IS NULL THEN
display_error; --I do stuff here to display error, not important to you
END IF;
END IF;
But I can't seem to get this to work. APEX doesn't find anything wrong with the code, it compiles without errors. But when I try it out, it throws an 'Invalid character' error, and the validation passes. I have tried perhaps making the entire a_lov query, adding an 'INTO x' and changing ':param' into 'apex_application.g_f02(i). But it just doesn't work.
Any ideas would be appreciated

Just wanted to post an update, actually got a solution going. And it was the stupidest thing ever.
The bit that is executed dynamically, I ended it with a semicolon, shouldnt have done that. That was all there was to it.
That one semicolon was what was messing me up, now it works. So if anyone stumbles upon this question, check your semicolons.

Related

ORA-06512 and ORA-01403 no data found

I keep getting the below error upon executing the tax_calc_sf function on SQL developer.
enter image description here
Also I am not sure why it always disconnects from database immediately after the process is exits.Any help with this would be appreciated.
CREATE OR REPLACE FUNCTION tax_calc2_sf
(p_id NUMBER,
p_sub NUMBER)
RETURN NUMBER
IS
lv_tax_num NUMBER(5,2) :=0;
BEGIN
SELECT p_sub*t.taxrate tax
INTO lv_tax_num
FROM bb_basket b, bb_tax t
WHERE b.shipstate = t.state
AND b.idBasket = p_id;
RETURN lv_tax_num;
END;
I have tried adding a exception block as below but that does not change anything but the same error as shown in the image above still repeats.
enter image description here
In the context of your [PL/SQL] function – tax_calc2_sf – the SELECT INTO statement should return precisely one row. If it does not return precisely one row, you will get one of the following predefined exceptions
NO_DATA_FOUND if the SELECT returns zero rows
TOO_MANY_ROWS if the SELECT returns more than one row
Adding an exception handler does not prevent the exception from occurring, it just allows you to perform custom handling.
So ask yourself, what should function tax_calc2_sf do if the SELECT returns no rows. Should the function return null? Should it return zero? Should it throw an exception?
Similarly, you need to decide what to do when the SELECT returns more than one row.
Alternatively, you could first perform a SELECT count(*) which will always return precisely one row and so will not throw any exceptions. Then you can decide how to proceed depending on whether the count is zero, one or more than one.
By the way, it is recommended to use ANSI join syntax when you query more than one database table.
SELECT p_sub*t.taxrate tax
INTO lv_tax_num
FROM bb_basket b
JOIN bb_tax t
ON b.shipstate = t.state
WHERE b.idBasket = p_id;

age control 2 for pl/sql

i am trying to find out at what age an employee started working.
if he started under 16 he should report this 'Error when entering the date of birth' mistake. so my trigger is created but my trigger is not working
I get ever this error: ORA-01422: Exact retrieval returns more than the requested number of lines
i can't find the problem
Here is the code:
SET SERVEROUTPUT ON
ACCEPT Birthday PROMPT ' Pleas give you Date of birth: '
CREATE OR REPLACE TRIGGER T_Controll
before INSERT ON meine_Firma -- Table
FOR EACH ROW
DECLARE
V_Berufstart meine_Firma.Hiredate%TYPE; --Job begin
V_Geburtsdatum DATE; -- Date of birth
V_Alter number:=0; -- AGE
SELECT HIREDATE INTO V_Berufstart FROM meine_Firma;
BEGIN
V_Geburtsdatum:=('&Birthday');
V_Alter:= Round(MONTHS_BETWEEN(V_Berufstart,V_Geburtsdatum)-2)/12;
IF 16 > V_Alter THEN
RAISE_APPLICATION_ERROR(-20201,'Error when entering the date of birth');
END IF;
END;
/
SET SERVEROUTPUT OFF
If he under 16 then he may not work
sorry my english is not good (=
You have a much bigger issue in this script than the error you are getting. Even after correcting as #ShaunPeterson suggested it will still fail
, it WILL NOT generate an error it will just not run as you expect. The issue is you failed to understand substitution variables - the use of &name (Specifically here &Birthday.) I'll actually use &Birthday in the following but the discussion applies to ANY/ALL substitution variables.
people fail to understand why they can't use the "&" substitution
variables in their PL/SQL procedures and functions to prompt for input
at run time. This article will hopefully help clarify in your mind
what the differences are so that you can understand where and when to
use these.
Substitution Variables The clue here is in the name... "substitution". It relates to values being substituted into the code
before it is submitted to the database. These substitutions are
carried out by the interface being used
The effect of this substitution is the the line containing the substitution variable is physically rewritten by the interface replacing %Birthday. In this case if you don't enter a value or the date 2000-05-19 the statement before and after substitution is
BEFORE: V_Geburtsdatum:=('&Birthday');
AFTER: V_Geburtsdatum:=(''); OR V_Geburtsdatum:=('2000-05-19');
Either way the after is what the compiler sees; it does NOT see %Birthday at all. Moreover when run the trigger will not prompt for a value. As far as the compiler is concerned it is a hard coded value that will never change. Beyond that a trigger, or any other PLSQL script (stored or anonymous) never prompts for values, they are actually incapable of doing so as it is not part of the language. Any prompt is via your interface software not plsql.
I'm going to suggest a way to avoid the trigger altogether. Getting on soap box: Triggers are BAD, they have some usefulness for assigning auto incrementing keys (before 12c),logging, very limited auditing, etc. However, for business rules they should be the option of last resort. Ok Get off soap box.
The first thing is to make the columns meine_Firma.Hiredate and meine_Firma.Geburtsdatum NOT null (if not already). If either are NULL you cannot calculate anything with them, the result would be NULL.
Second create a new column age_at_hire (or whatever) as a virtual column then put a check constraint on it. And voila trigger no longer needed. See fiddle for demo.
So the proposed change (YES you will probably have to clean up the bad data first):
alter table meine_Firma modify
( hiredate not null
, Geburtsdatum not null
) ;
alter table meine_Firma add
( age_at_hire integer generated always as (trunc(months_between(hiredate,Geburtsdatum))) virtual
, constraint check_age_at_hire check (age_at_hire >= 16*12)
);
Anyway, I hope you get an understanding of substitution variables for the future. And learn to avoid triggers. Good luck.
The reason you are getting that specific error is that the below select will select ALL rows from meine_Firma as there is no where clause
SELECT HIREDATE INTO V_Berufstart FROM meine_Firma;
However because you are in a trigger you do not need to select anything you use the :NEW bind variable. So you can just use
V_Berufstart := :NEW.HIREDATE;
If this was an update trigger there would be both a :NEW and :OLD bind variable declared so that you can access the OLD and NEW values. As this is an Insert trigger the :OLD will just be null as there is no old values.

PL/SQL: Run package after checking all rows in trigger

I have an inventory table with expected quantities and actually received quantities. Let's say inv.q_ex and inv.q_rd.
The INSERT to the table has a positive value in q_ex and a zero in q_rd because it hasn't arrived yet. I'd like to run a package when I detect that the q_rd value changes from 0 to something else, indicating it's been received and stored.
Making a trigger to detect after update and checking each row is easy, but I'm not sure how to ensure it only runs once.
The skeleton is:
CREATE OR REPLACE TRIGGER example
AFTER UPDATE ON inv
FOR EACH ROW
BEGIN
IF :OLD.q_rd = 0 AND :NEW.q_rd > 0 THEN
pkg.proc();
END IF;
END;
/
The problem I see is I only want it to run one time. I just need to identify when it needs to be executed. Ideally, on the first row where my condition is met, I would exit the loop (seems like a waste to keep checking when I already know I need to execute) and call my procedure.
I couldn't find a way to "exit" the for each and treat it as a normal AFTER UPDATE, so then I tried using both BEFORE UPDATE and AFTER UPDATE. The BEFORE portion would check each row and update a boolean. The AFTER portion would wait for that to happen and if it was true, call the procedure.
CREATE OR REPLACE TRIGGER example
BEFORE UPDATE ON inv
FOR EACH ROW
DECLARE
shouldExecute BOOLEAN;
BEGIN
IF :OLD.q_rd = 0 AND :NEW.q_rd > 0 THEN
shouldExecute := TRUE;
END IF;
END;
AFTER UPDATE ON inv
BEGIN
IF shouldExecute THEN
pkg.proc();
END IF;
END;
/
I suspect this wouldn't work anyway because, according to the syntax, it redeclares the boolean variable on each row. I thought that maybe I could make it "global" but regardless, turns out I can't add both BEFORE and AFTER to the same trigger for some reason (unless I didn't research enough), so I broke it out into two triggers. The problem now is I can't share that boolean between the two triggers. Can I share the value, or am I going about this all wrong?
There's a lot of little questions here so I'll try to answer them all :)
Regarding "FOR EACH ROW", Oracle triggers support two different triggering methods, STATEMENT or ROW. If you include the "FOR EACH ROW" in the definition, you'll get a row trigger, which is fired once per row affected by the query, which is what you seem to want here. Statement level triggers get fired only once for each query. An advantage to using row triggers is that you can use the :OLD and :NEW metavariables which refer to the previous and current row values.
As you've discovered, you can't add BEFORE and AFTER to the same trigger - you'll need to break them out into two triggers.
Unfortunately there isn't a simple way of sharing the boolean variable between the two triggers. The easiest way is probably to create a package with a public variable, which you can set in the BEFORE trigger, and check in the AFTER trigger.
The package would look something like this:
CREATE OR REPLACE PACKAGE PCKG_DEMO
AS
shouldExecute BOOLEAN;
END;
/
Then your BEFORE UPDATE trigger:
CREATE OR REPLACE TRIGGER beforeexample
BEFORE UPDATE ON inv
FOR EACH ROW
BEGIN
IF :OLD.q_rd = 0 AND :NEW.q_rd > 0 THEN
PCKG_DEMO.shouldExecute := TRUE;
END IF;
END;
/
And your AFTER UPDATE trigger:
CREATE OR REPLACE TRIGGER afterexample
AFTER UPDATE ON inv
FOR EACH ROW
BEGIN
IF PCKG_DEMO.shouldExecute THEN
pkg.proc();
END IF;
END;
/
Those are a bit pseudocode-y - I don't have access to an Oracle database right now. You can read more about triggers here. Hope this helps!
You need to do more research. The link above takes you to trigger discussion for Oracle 9. I hope you are not actually using that version; support ended in 2007. Since version 11g Oracle has provided "Compound Triggers" where you can fire the same trigger both Before and After for both statement and row level. Compound triggers do allow sharing variables between the different invocations.

ORA-00984 on an after update pl/sql trigger

I have been fighting with this trigger for a while now, im trying to send to the table "MOVIMIENTO" the old stock, the new one, the difference between them and what kind of operation it is, only problem it's that i get an ORA-00984 error on line 12 and a sql statement ignored on line 11, any ideas?
CREATE OR REPLACE TRIGGER TRG_MOVIMIENTO
AFTER UPDATE OF "STOCK" ON PRODUCTO
FOR EACH ROW
DECLARE
v_tmov VARCHAR2(7);
v_cant NUMBER(6);
BEGIN
v_cant:=:OLD.STOCK-:NEW.STOCK;
IF :OLD.STOCK>:NEW.STOCK THEN
v_tmov:='SALIDA';
ELSE
v_tmov:='ENTRADA';
END IF;
INSERT INTO MOVIMIENTO(N_MOV,TIPO_MOV,STOCK_NEW,STOCK_OLD,CANT_MOV)
VALUES(s_NUMEROMOV.nextval,v_tmod,:NEW.STOCK,:OLD.STOCK,v_cant);
END;
/
Is v_tmod a column on the MOVIMIENTO table. Because there is no variable with that name. That would explain the error.
Edit: OK, looking at the comments, I think I know what is going on.
The variables in the declare section are v_tmov and v_cant.
But in your INSERT statement you have made a typo and used v_tmod.
Oracle sees this is not a known variable so assumes it must be a column name. So it then reports the ORA-00984 error, without even checking to see if its a valid column name. This is because you cannot use a column here.
So the real error is v_tmod is not a defined variable

Value of a NEW variable on a trigger not changing, on plsql

I'm loading data through Oracle Apex utilities using a datasheet.
I want to make a trigger that checks for a value on the table from the data loaded, and then changes it depending on what it gets.
The table has 4 columns: id,name,email,type
The data to load is something like this: name,email,type
Now my trigger:
create or replace TRIGGER BI_USER
before insert ON USER
for each row
declare
begin
if :NEW.ID is null then
select USERID_SEQ.nextval into :NEW.ID from dual;
end if;
:NEW.TYPE := 'something else';
end;
The ID works great, it takes a number from the sequence, but :new.type isn't working, it doesn't change.
I also run the SQL insert separately and the same happens.
EDIT:
new.type type is char(1), I wrote it like this just for testing yet it doesn't change...
aah I'm dissapoint of myself, it throws the error just after reading the data and never fires the trigger.
What I was trying to do is that it will have the name of the TYPE column, and put the id from that table into the NEW.type
Is there a way to change the NEW type?
I see what you're trying to do. You want your table to accept an inserted record containing data that will not fit in the width of one of the fields, and you want to use a trigger to "fix" the data so that it will fit.
Unfortunately, this trigger will not help you because the data is validated before your triggers are fired.
An alternative way to get around this may be to use a view with an instead-of trigger. The view would have a column "TYPE" which is based on a string of length 9; the instead-of trigger would convert this to the CHAR(1) for insert into the underlying table.
Try this instead:
select 'something else' into :NEW.TYPE from dual;
If this syntax worked for ID it should also work for TYPE

Resources