Trigger not executing correctly in Oracle PL/SQL - oracle

I have a trigger which executes on AFTER UPDATE. It doesn't work as i want it to.
How would i check if a value has changed on a field which is of nullable type? I have the following fields which are of nullable type:
FRM_DATE DATE
FRM_TIME DATE
THE_DATE DATE
THE_TIME NUMBER(4,2)
THE_BOOL NUMBER(2)
I would like to execute a set of logic only if the value for the above fields have actually changed. If the values are the same then i do not want to code to execute. So from the UI, lets say if one of the fields had a value and the user removes it(it now becomes NULL) and hits submit button, i want my logic to execute because a change has been made.
I tried the following but it doesn't execute the logic i want:
IF (nvl(:old.FRM_DATE, '') <> nvl(:new.FRM_DATE,'')) THEN
--My logic
END IF;
I also tried
IF (nvl(:old.FRM_DATE, NULL) <> nvl(:new.FRM_DATE,NULL)) THEN
--My logic
END IF;
Any ideas?
Kind Regards,

In Oracle, null and the empty string '' are effectively equivalent:
Oracle Database currently treats a character value with a length of zero as null. However, this may not continue to be true in future releases, and Oracle recommends that you do not treat empty strings the same as nulls.
... so the two checks are really the same. In both cases you're doing nvl(something, null), which doesn't make much sense - you're saying "if the value is null then make it null", which is redundant and doesn't do any real transformation. So if either the old or new value is null you're still trying to compare null with either itself or a non-null value; and as San said, you can't compare null with anything using equality conditions.
You could use a magic value, again as San shows, but you have to be sure that can never actually appear in the data. It may be safer, and make the intent clearer, if you explicitly check with is null:
IF (:old.FRM_DATE IS NULL AND :new.FRM_DATE IS NOT NULL)
OR (:old.FRM_DATE IS NOT NULL AND :new.FRM_DATE IS NULL)
OR :old.FRM_DATE != :new.FRM_DATE
THEN
...

You cannot compare null with null or null with any other value, change the logic to:
IF (nvl(:old.FRM_DATE, to_date('01-01-1900', 'dd-mm-yyyy')) <> nvl(:new.FRM_DATE,to_date('01-01-1900', 'dd-mm-yyyy'))) THEN
--My logic
END IF;

Just in case, additional recommendations. Check type of trigger you use?
I mean, row trigger or statement trigger.
You can also see an example of a trigger on the column:
Oracle SQL trigger on update of column

While it is true that Oracle considers (for the time being) an empty string and NULL to be equivalent, the nice people at Oracle have provided us with an easy way to catch a change -- but it may not be what you want.
The updating system function returns true if the column appears on the left side of the "=" in the set clause. So
update table1
set col1 = null,
col2 = 42
where ...;
Within the update trigger, updating( 'col1' ) and updating( 'col2' ) will both return true. But they will return true even if col1 started out with a null value (or '') or col2 was already 42.
So it would appear you have two options: either you can, like Oracle, consider NULL and '' to be equivalent and don't flag them as a change, or you can flag any attempt to make a change in the field even if it turns out the same value was actually being rewritten to the field.
Unless there is an objective business requirement mandating one over the other, it really doesn't matter which way you (or whoever makes these decisions) choose, as long as you publicize the behavior to everyone who needs to know.

Related

ORACLE SQL Replace null values with previous value depending on other fields

From a query I want to replace the null values of the "cod_account" field, the condition to fill this field is that it must take the previous value only if it satisfies that the previous records of other fields (cod_agen, cod_sub, no_prod) are equal.
Currently it is like this:
enter image description here
what is desired is
enter image description here
thanks for your advice. i solve it
use with to separate queries.
select
case when cod_account is null and class ='Primary'
then null
when cod_account is null and class='Secundary'
then (select cod_account from principals P--principals is table create in a "with"
where fa.cod_agen=P.cod_agen
and fa.cod_sub=P.cod_sub
and fa.no_prod=P.no_prod
and p.class='secundary'
)
...
from signs fa

Why is this check constraint not working when it checks length?

create table test
(
id varchar2(10) check( length(trim(id)) > 0),
primary key(id)
)
insert into test values (' '); -- this works
I expected this check constraint to stop this insert. But, it gets inserted none the less. Why ?
Problem is TRIM(' ') returns NULL and LENGTH(NULL) also gives NULL (not 0)
Try NVL(LENGTH(TRIM(' ')), 0) > 0 or
Try TRIM(ID) IS NOT NULL
Snippet stating that constraints can return null aka 'unknown' and considered to be NOT VIOLATING the constraint. Only a return of false violates a constraint. At least as per Oracle 12c
See Oracle Concept page about constraints
Explanation for what you saw:
If str is made up entirely of spaces, then trim(str) is the empty string.
Oracle treats the "empty string" the same as null (of varchar2 data type, when that matters), in flagrant violation of the SQL standard. Oracle is not even consistent in this - there are (very few) exceptions where the empty string is, in fact, seen as "empty string" (for example, in concatenations).
By definition, the length of null is null (in particular, not zero).
In SQL, a condition like null > 0 evaluates to unknown (in the three-valued logic needed to accommodate null in such conditions).
In check constraints unknown is treated the same as true. This is different from the treatment in other conditions (in SQL statements - in where clauses, join conditions etc.), where unknown is treated the same as false. This is documented, for example, here: https://docs.oracle.com/cd/B19306_01/server.102/b14200/clauses002.htm - see the first paragraph in the Check Constraints section.
The correct solution to your problem:
Checking if something is null should be done DIRECTLY, with the is null or is not null conditions. Don't use length for that.
Like this:
check( trim(id) is not null )

How to make a checkbox have checked be non null and unchecked be null

I have a date column that i've turned into a checkbox. How do I make it so when the date isn't null the checkbox is checked and if it is null then make it unchecked?
I'd rather keep my DATE column as is, but make it hidden. Create another - checkbox item - and create (two?) trigger(s):
POST-QUERY (which fires when you query existing data)
WHEN-VALIDATE-ITEM (which fires when you modify data on the screen)
if you keep the date column hidden, maybe you don't need it. Or, you do if that date column is modified by some process
Trigger code is simple (presuming that 0 means "not checked" and 1 means "checked"):
:block.checkbox_item := case when :block.date_column is null then 0
else 1
end;
If your Forms version doesn't speak CASE, use DECODE instead (it requires the SELECT statement):
select decode(:block.date_column, null, 0, 1)
into :block.checkbox_item
from dual;

Before update trigger is not creating history for null values

My before update trigger is not creating any history for changing null value to any other value. It is creating history for changing any other values.
My main logic is like this:
if nvl(:old.place,null) <> nvl(:new.place,null) then
insert into table (place)
values(:old.place)
end if;
The NVL() function is used to substitute a value in place of null. You're replacing null with... null, which is pointless. You're then trying to compare two null values with the (in)equality operator, and as null is neither equal to or not equal to anything including itself, the result is unknown, and your condition is always false if either the old or new value is null.
You could use a fixed dummy value that you know will never actually exit:
if nvl(:old.place,'**fake**') <> nvl(:new.place,'**fake**') then
But for clarity I'd generally prefer to explicitly test for nulls:
if (:old.place is null and :new.place is not null)
or (:old.place is not null and :new.place is null)
or (:old.place != :new.place) then

Insert default value when null is inserted

I have an Oracle database, and a table with several not null columns, all with default values.
I would like to use one insert statement for any data I want to insert, and don't bother to check if the values inserted are nulls or not.
Is there any way to fall back to default column value when null is inserted?
I have this code:
<?php
if (!empty($values['not_null_column_with_default_value'])) {
$insert = "
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column,:not_null_column_with_default_value)
";
} else {
$insert = "
INSERT INTO schema.my_table
( pk_column, other_column)
VALUES
(:pk_column,:other_column)
";
}
So, I have to omit the column entirely, or I will have the error "trying insert null to not null column".
Of course I have multiple nullable columns, so the code create insert statement is very unreadable, ugly, and I just don't like it that way.
I would like to have one statement, something similar to:
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column, NVL(:not_null_column_with_default_value, DEFAULT) );
That of course is a hypothetical query. Do you know any way I would achieve that goal with Oracle DBMS?
EDIT:
Thank you all for your answers. It seams that there is no "standard" way to achieve what I wanted to, so I accepted the IMO best answer: That I should stop being to smart and stick to just omitting the null values via automatically built statements.
Not exactly what I would like to see, but no better choice.
For those who reading it now:
In Oracle 12c there is new feature: DEFAULT ON NULL. For example:
CREATE TABLE tab1 (
col1 NUMBER DEFAULT 5,
col2 NUMBER DEFAULT ON NULL 7,
description VARCHAR2(30)
);
So when you try to INSERT null in col2, this will automatically be 7.
As explained in this AskTom thread, the DEFAULT keyword will only work as a stand-alone expression in a column insert and won't work when mixed with functions or expressions such as NVL.
In other words this is a valid query:
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column, DEFAULT)
You could use a dynamic query with all rows and either a bind variable or the constant DEFAULT if the variable is null. This could be as simple as replacing the string :not_null_column_with_default_value with the string DEFAULT in your $insert.
You could also query the view ALL_TAB_COLUMNS and use nvl(:your_variable, :column_default). The default value is the column DATA_DEFAULT.
I think the cleanest way is to not mention them in your INSERT-statement. You could start writing triggers to fill default values but that's heavy armor for what you're aiming at.
Isn't it possible to restructure your application code a bit? In PHP, you could construct a clean INSERT-statement without messy if's, e.g. like this:
<?php
$insert['column_name1'] = 'column_value1';
$insert['column_name2'] = 'column_value2';
$insert['column_name3'] = '';
$insert['column_name4'] = 'column_value4';
// remove null values
foreach ($insert as $key => $value) {
if (is_null($value) || $value=="") {
unset($insert[$key]);
}
}
// construct insert statement
$statement = "insert into table (". implode(array_keys($insert), ',') .") values (:". implode(array_keys($insert), ',:') .")";
// call oci_parse
$stid = oci_parse($conn, $statement);
// bind parameters
foreach ($insert as $key => $value) {
oci_bind_by_name($stid, ":".$key, $value);
}
// execute!
oci_execute($stid);
?>
The better option for performance is the first one.
Anyway, as I understand, you don't want to repeat the insert column names and values due the difficult to make modifications. Another option you can use is to run an insert with returning clause followed by an update:
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column, :not_null_column_with_default_value)
RETURNING not_null_column_with_default_value
INTO :insered_value
It seems to work with PHP.
After this you can check for null on insered_value bind variable. If it's null you can run the following update:
UPDATE my_table
SET not_null_column_with_default_value = DEFAULT
WHERE pk_column = :pk_column:
I would like to use one insert
statement for any data I want to
insert, and don't bother to check if
the values inserted are nulls or not.
Define your table with a default value for that column. For example:
create table myTable
(
created_date date default sysdate,
...
)
tablespace...
or alter an existing table:
alter table myTable modify(created_date default sysdate);
Now you (or anyone using myTable) don't have to worry about default values, as it should be. Just know that for this example, the column is still nullable, so someone could explicitly insert a null. If this isn't desired, make the column not null as well.
EDIT: Assuming the above is already done, you can use the DEFAULT keyword in your insert statement. I would avoid triggers for this.

Resources