ORA 00920 In CASE with "OR" inside Trigger Oracle - sql

The syntax error seems to be in the WHEN INSERTING OR UPDATING (more specifically it underlines the OR), I don't understand why it doesn't work in a CASE condition but it works in a IF condition.
Its a BEFORE DELETE OR INSERT OR UPDATE Trigger
Code portion of the issue:
SELECT
field
INTO
my_var
FROM
my_table
WHERE
column1 = (CASE
WHEN INSERTING OR UPDATING THEN
:new.column2
ELSE --deleting
:old.column2
END);
What would be the solution?
Here is the rest of the trigger if anyone wants to test : https://pastebin.com/AJqGQyG8
Edit :
The issue seems to be that the WHEN condition needs an operator, so I tried using :
WHEN INSERTING = TRUE
But that just resulted in another error :
ORA-00904: "TRUE": Invalid identified

As Thorsten explained already, inserting and updating and deleting are predicates (expressions of BOOLEAN data type) which exist only in the PL/SQL code part of the trigger. They can't exist (or be used in any way) in a SQL statement, since Oracle SQL does not recognize/implement the BOOLEAN data type.
Moreover, :new.<whatever> and :old.whatever are also available only in the PL/SQL portion of your trigger; if you have embedded SQL, anything like :new.<whatever> is interpreted as a bind variable, not special in any way, and you will be prompted for a value (if you use an "advanced" front-end program) or you will simply get an error telling you that not all variables are bound.
Thorsten has already shown one way to do what you want to do. If you really need (want?) to use a case expression, you can do something like this:
Declare a variable (like v_value in Thorsten's answer) in the DECLARE section of the trigger. Assign the proper data type to it.
Then in the execution block write something like this, BEFORE the SQL statement:
v_value := case when inserting or updating then :new.column2 else :old.column2 end;
and then in the SQL statement compare to v_value, also as in Thorsten's answer.
Alternatively, you can assign the value to v_value in the DECLARE block, right as you declare it, in the usual (PL/SQL) way. In any case, the value must be assigned outside the SQL statement, not in it.

I think the problem is that PL/SQL knows these boolean variables inside a trigger, but Oracle's SQL doesn't know BOOLEAN unfortunately.
One solution may be:
IF INSERTING OR UPDATING THEN
v_value := :new.column2;
ELSE
v_value := :old.column2;
END IF;
...
SELECT field INTO my_var FROM my_table WHERE column1 = v_value;

Related

need help writing this compound trigger

i have a column in my table that i need to update on insert or update, i can't do it with a simple trigger because the new value has to be SELECTED from the same table and other tables (which would generate trigger is mutating error).
my solution was to use a compound trigger, after a little bit of documentation i wrote this code:
CREATE OR REPLACE TRIGGER update_contract_n FOR
INSERT OR UPDATE ON contract
COMPOUND TRIGGER
v_n VARCHAR(70);
AFTER EACH ROW IS BEGIN
v_n := object_contract(:new.id_contract);
END AFTER EACH ROW;
AFTER STATEMENT IS BEGIN
UPDATE contract
SET
n = v_n
WHERE
id_contract = :new.id_contract;
END AFTER STATEMENT;
END update_contract_n;
the function object_contract() joins 4 tables one of which is the table contract and returns a varchar.
when i compile the trigger code i get this error :
pls-00679 trigger binds not allowed in before/after statement section
but i don't know what i'm doing wrong since the variable binding was done in AFTER EACH ROW section not in AFTER STATEMENT.
any help would be appreciated (fixing or rewriting the code), a simple guide on how to use a compound trigger would also be appreciated.
EDIT:
i found the problem, i was referencing :new.id_contract in AFTER STATEMENT and i fixed it, but now i am getting table is mutating trigger/function may not see it
how can i fix that?

"Catching" query from oracle forms

I would like to know if it is possible and how to "catch" query
or WHERE part of SELECT after i enter value and press F8 to excecute query,
is it possible to catch that query and how.
tnx
Use get_block_property built-in. One of its parameters is last_query which
Returns the SQL statement of the last query in the specified block
Also, have a look at default_where and onetime_where parameters.
For more info, see Online Forms Help system (now that you know what to search for).
You can also create a POST-QUERY trigger and in it add code similar to the following:
DECLARE
lastSuccessfulQuery varchar2(4000);
BEGIN
lastSuccessfulQuery := :SYSTEM.LAST_QUERY;
--
-- Do something with the value here
--
END;

How to check updating of column value in oracle trigger

I'm using UPDATING(col_name) to check if column's value is updated or not inside the trigger. But the big problem is this command won't check value of :old and :new objects. UPDATING(col_name) is true if col_name existed in set part of query even with old value.
I don't want to check :old.col1<>:new.col1 for each column separately.
How can I check changing column value correctly?
I want to do this in a generic way. like :
SELECT col_name bulk collect INTO included_columns FROM trigger_columns where tbl_name ='name';
l_idx := included_columns.first;
while (l_idx is not null)
loop
IF UPDATING(included_columns(l_idx)) THEN
//DO STH
return;
END IF;
l_idx := included_columns.next(l_idx);
end loop;
Thanks
IN a comment you said:
"I want to do this in a generic way and manage it safer. put columns which are important to trigger in a table and don't put many IF in my trigger. "
I suspected that was what you wanted. The only way you can make that work is to use dynamic SQL to assemble and execute a PL/SQL block. That is a complicated solution, for no material benefit.
I'm afraid I laughed at your use of "safer" there. Triggers are already horrible: they make it harder to reason about what is happening in the database and can lead to unforeseen scalability issues. Don't make them worse by injecting dynamic SQL into the mix. Dynamic SQL is difficult because it turns compilation errors into runtime errors.
What is your objection to hardcoding column names and IF statements in a trigger? It's safer because the trigger is compiled. It's easier to verify the trigger logic because the code is right there.
If this is just about not wanting to type, then you can generate the trigger source from the data dictionary views (such as all_tab_cols) or even your own metadata tables if you must (i.e. trigger_columns).
You can define a global function similar to the following:
CREATE OR REPLACE FUNCTION NUMBER_HAS_CHANGED(pinVal_1 IN NUMBER,
pinVal_2 IN NUMBER)
RETURN CHAR
IS
BEGIN
IF (pinVal_1 IS NULL AND pinVal_2 IS NOT NULL) OR
(pinVal_1 IS NOT NULL AND pinVal_2 IS NULL) OR
pinVal_1 <> pinVal_2
THEN
RETURN 'Y';
ELSE
RETURN 'N';
END IF;
END NUMBER_HAS_CHANGED;
Now in your trigger you just write
IF NUMBER_HAS_CHANGED(:OLD.COL1, :NEW.COL1) = 'Y' THEN
-- whatever
END IF;
Note that this function is defined to return CHAR so it can also be called from SQL statements, if needed - for example, in a CASE expression. Remember that in Oracle, there is no BOOLEAN type in the database - only in PL/SQL.
You'll probably want to create additional versions of this function to handle VARCHAR2 and DATE values, for a start, but since it's a matter of replacing the data types and changing the name of the function I'll let you have the fun of writing them. :-)
Best of luck.

Run an oracle SQL script twice with different parameters

I have an SQL statement in Oracle SQL developer that has some variables:
DEFINE custom_date = "'22-JUL-2016'" --run1
DEFINE custom_date = "'25-JUL-2016'" --run2
SELECT * FROM TABLE WHERE date=&custom_date
The real query is much more complicated and has many more variables and new tables are created from the results of the query. How can I create a script so that the query is executed twice, the first time with the custom date set as in the first line and the second time as in the second line.
In Oracle, the &variable is a "substitution variable" and is not part of SQL; it is part of the SQL*Plus scripting language (understood by SQL Developer, Toad etc.)
The better option, and what you are asking about, is BIND VARIABLES. The notation is :variable (with a colon : instead of &), for example :custom_date.
The difference is that a substitution variable is replaced by its value in the front-end application (SQL Developer in your case) before the query is ever sent to the Oracle engine proper. A bind variable is substituted at runtime. This has several benefits; discussing them is outside the scope of your question.
When you execute a query with bind variables in SQL Developer, the program will open a window where you enter the desired values for the bind variables. You will have to experiment with that a little bit till you can make it work (for example I never remember if a date must be entered with the single quotes or without). Good luck!
Define is used in TRANSACT SQL. To do this Oracle way, You can create anonymus PL/SQL block, similar to this:
DECLARE
p_param1 DATE;
p_param2 NUMBER;
CURSOR c_cur1(cp_param1 DATE,cp_param2 NUMBER)
IS
SELECT * FROM table WHERE date = cp_param1
;
BEGIN
-- Execute it first time
p_param1 := TO_DATE('2016-09-01','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
-- Execute it second time
p_param1 := TO_DATE('2015-10-11','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
END;
And in it, You create cursor with parameters and execute it twice with different parameter.
I do not know why You want to execute this query twice, so the script abowe does nothing with results, but it certainly should execute Your query twice, with different params.

Retrieving the return value of an insert...value 'return into' statement using codeigniter (oracle oci 8)

Firstly I apologise if this is a duplicate!
Say I have a PL/SQL anonymous block stored in a PHP variable $sql, ready to be executed and is laid out thus:
BEGIN
INSERT INTO TABLE_NAME(PARAM1, PARAM2, PARAM3)
VALUES(?,?,?)
RETURNING TABLE_NAME.PARAM1 INTO ?;
END;
Using the Codeigniter framework, I want to be able to perform this statement:
$query = $this->db->query($sql, array('1', 'Hello', 'World', $id));
...and then be able to access the returned variable of '1', which should be stored in the $id variable.
I know that the syntax I have provided wouldn't necessarily work, I've just used it to demonstrate what I'm trying to achieve.
I don't believe the OCI8 driver is equipped to handle returning variables from a statement in this way. I can't seem to find a way to manually bind a variable (in this case $id) to the RETURN...INTO clause of the PL/SQL statement. Normally I could do:
oci_bind_by_name($stid, ':id', $id) //replace '?' with ':id' on the RETURN...INTO section of the anonymous block
...However, I have tried to implement this using the $this->db->call_function() but that did not work either. I have been digging around the code to try and find a way to extend the driver to do this but I fear I may be complicating things or I am going about it the wrong way.
EDITED QUESTION 8/4/14:
I know how to select a column variable I want to return. What I'm unsure about is how I can bind that returned variable to a PHP variable inside the codeigniter framework.
Super thanks in advance! :)
EDIT 1:
I've edited the question to be clearer (emboldened above.)
I'm using the returned value as a foreign key in subsequent statements.
I'm auto incrementing the 'PARAM1' column using a trigger and a sequence.
Oracle has a unique identifier in all tables called rowid.
pleasce check the following select statement
select rowid, *
from ANYTABLE;
you can get the rowid of the inserted record by
BEGIN
INSERT INTO TABLE_NAME(PARAM1, PARAM2, PARAM3)
VALUES(?,?,?)
RETURNING rowid INTO my_output_variable_;
END;