inserting mutiple column values into single insert value? - sql

So I have this University assignment in which I have to create a trigger called bill_overdue. When a row that has status = overdue is inserted into table invoice, a row is inserted into another table called message.
CREATE SEQUENCE AUTOINCREMENTMESSAGE
MINVALUE 100
START WITH 101
INCREMENT BY 1
CACHE 10
;
CREATE OR REPLACE TRIGGER BILL_OVERDUE
BEFORE INSERT ON INVOICE
FOR EACH ROW
WHEN (NEW.STATUS = 'Overdue')
BEGIN
INSERT INTO MESSAGE (MESSAGENO,MESSAGEDATE,ORIGIN,MESSAGE)
VALUES (AUTOINCREMENTMESSAGE.nextval,SYSDATE,USER,:NEW.DATEISSUED,:NEW.INVOICENO,:NEW.CAMPAIGNTITLE);
END;
/
Now as you can see I want to add :new.dateissued, :new.invoiceno and :new.campaigntitle into a single field(message).Now I know that what I have done is wrong but I've tried adding parentheses around it etc and nothing seems to do what I want. How do I get this to work? Is it possible to do what I want or have I got it completely wrong?

You could use concatenation
:new.dateissued || ', ' || :new.invoiceno || ', ' || :new.campaigntitle

For Oracle you can also concatenate using the CONCAT() function and you can try
CONCAT(:NEW.DATEISSUED,:NEW.INVOICENO,:NEW.CAMPAIGNTITLE)
You may have to cast/convert some of those values though.

Related

how to make sure that trigger generated value is being returned?

I have this INSERT query, which purpose is to insert the one row in my database.
Similarly I also have a INSERT query which insert multiple rows.
One of the columns in the table is generated after the values has been generated, since it combines a set of column values to construct a name. The name itself it generated from a Trigger, and its triggered After insert, since the column values has to exist for me to generate the name.
my problem now is when I insert one row or multiple rows, I want to know the the generated column value, but when I return it, it states its null?
#$"INSERT INTO registration_table (id, ...,)
VALUES (1,...,)
RETURNING row_id, name;";
which in return gives me an id the one I inserted, but the not actual name but instead I get null..
The trigger is pretty straight forward
CREATE TRIGGER name_insert_trigger
AFTER INSERT
ON registration_table
REFERENCING NEW TABLE AS new_inserts
FOR EACH STATEMENT
WHEN (pg_trigger_depth() = 0)
EXECUTE PROCEDURE registration_entry_name();
CREATE OR REPLACE FUNCTION registration_entry_name()
RETURNS trigger AS
$$
DECLARE
BEGIN
UPDATE registration_table
SET name = |Pattern| -- This one being the actual name generated..
FROM new_inserts
WHERE new_inserts.row_id = registration_table.row_id;
RETURN null;
END;
$$
LANGUAGE plpgsql;
but the insert query above does not return the name?
why not?
You actually need a BEFORE trigger, your data values will be there. The designation of Before and After very often causes misconceptions especially of row level triggers. The terms do not indicate their timing in relation to the DML. I have found it useful to think of them as "before final data values are set" and "after final data values are set" but both run before the invoking DML completes (for now we will bypass deferred triggers). Lets look at inserts. When the before row trigger fires the NEW row contains the values at that point for every column in the row, any value not specified in the statement will be null or contain the specified default if any. Before row triggers can can change any column. After row triggers cannot change columns, if present any change is ignored.
Your description and code imply you need to combine a couple columns to generate the content of another. Since you did not specify exactly that I will build an example and demo.
create table users ( usr_id integer generated always as identity
, lname text not null
, fname text not null
, full_name text not null
) ;
create or replace
function users_bir()
returns trigger
language plpgsql
as $$
begin
if new.full_name is null
then
new.full_name = trim(new.fname) || ' ' || trim(new.lname);
end if;
return new;
end;
$$;
create trigger users_bir_trg
before insert on users
for each row
execute procedure users_bir();
insert into users(fname, lname)
values ( 'George', 'Henery')
, ( 'Samatha', 'van Horm');
insert into users(fname, lname, full_name)
values ( 'Wacky', 'Warriors','Not so tough guys');
This setup allows the full_name to be specified or generated. If only generation is desired remove the IF leaving only the assignment statement. Even better if you have Postgres 12 or higher just define the the column as a generated column. This is also in the demo.

Adding (count) sequence value to column value via trigger in Oracle SQL

I would like to write a trigger which will be able to add sequence value to column value.
Example: Sequence value is 5 and column value in table is 45. Result after inserting new row should be 50.
Problem is that I dont know how to fetch value from the column and then add it to the sequence value in trigger code.
After using my trigger I am able to insert a new row which will create columns with value = 5.
CREATE SEQUENCE Adding_Seq
START WITH 5
INCREMENT BY 1;
CREATE OR REPLACE TRIGGER Adding_Trigger
BEFORE INSERT ON Table
FOR EACH ROW
BEGIN
SELECT Adding_Seq.nextval
INTO :new.Value
FROM dual;
END;
I tried to add some "+" in section SELECT or later in INTO with name of the column from table, but there were errors.
Is is possible to do something like this in trigger or not?
Thanks for you help!
This is really strange. But you can just use addition:
BEGIN
SELECT :new.Value + Adding_Seq.nextval INTO :new.Value
FROM dual;
END;

Check if VARCHAR2 contains only alphabets using trigger

I need to write such a trigger that will check name of the person and will print out his/her id if those people have any digits in theirs names.
What I have by now:
set SERVEROUTPUT ON
create or replace trigger BeforeUpdate
Before insert on customer
for each row
declare
n varchar2(10);
counter number;
nextR number:=0;
begin
select count(id_customer) into counter from customer;
LOOP
nextR:= nextR +1;
select cname into n from customer where id_customer = nextR;
if n not like '%[0-9]%' then
DBMS_OUTPUT.PUT_LINE(nextR || ' has incorrect name');
end if;
exit when nextR = counter;
end loop;
end;
/
It compiles and when I am trying to fire this trigger it do nothing.
I will be grateful for any help.
There are a couple of problems in your code:
using dbms_output in a trigger doesn't really make sense; usually, the INSERT will be performed by client code that doesn't handle the console output.
The sensible thing is to raise an exception instead.
You don't need to perform a SELECT in your trigger code. In fact, doing so will usually either be superfluous or raise a mutating table error. Instead, use :new and :old to refer to the values of the row that was inserted
(minor) naming a before insert trigger BeforeUpdate is somewhat confusing
use a regular expression for testing this business rule (seriously; regexes rule for this kind of thing)
Altogether, here's the fixed version (untested, I don't have an Oracle instance available for testing right now):
create or replace trigger TR_BI_CUSTOMER
Before insert on customer
for each row
begin
if regexp_like(:new.name, '.*[0-9].*') then
raise_application_error(-20001, 'Incorrect name: ' || :new.name);
end if;
end;
Use regular expression to get your result.
In your case, if you get a digit in n, your if clause should be executed.
So,
if regexp_replace(n,'[^[:digit:]]') IS NOT NULL then
DBMS_OUTPUT.PUT_LINE(nextR || ' has incorrect name');
end if
It seems you are also attempting to use a regular expression for digit. However, what your code is searching is for a string that has [0-9] in it. Like Bat[0-9]Man, which is not your desired result.
In my code, whatever expression is not digit in the given name is being replaced. If the name does not contain any digits, the regular expression would return null. If there is any digit at any place,the expression would return those digits.
You could analyse the following query for better grasping of what is happening here:
select regexp_replace(cname,'[^[:digit:]]') OUTP, cname from customer;
EDIT :
This is not how you write a trigger !
The trigger will be fired each time an insert is going to take place. You don't need the counter. You need to use :NEW reference
set SERVEROUTPUT ON
create or replace trigger update or
Insert on customer
for each row
begin
if regexp_replace(:NEW.cname,'[^[:digit:]]') IS NOT NULL then
DBMS_OUTPUT.PUT_LINE(nextR || ' has incorrect name');
end if;
end;
/
This is a job for REGEXP_LIKE()! The regex of '\d' matches a number.
SQL> with tbl(id, name) as (
select 1, 'Batman' from dual union
select 2, 'Robin1' from dual union
select 3, 'Supe4rman' from dual union
select 4, '3Joker' from dual
)
select id, name bad_name
from tbl
where regexp_like(name, '\d');
ID BAD_NAME
---------- ---------
2 Robin1
3 Supe4rman
4 3Joker
SQL>
If your goal is to strip out the digits on the way in (but be careful, a company really could have a number in the name like Level3 Communications or 3Com, if it's a person its less likely but these days who knows!) This is untested:
CREATE OR REPLACE TRIGGER customer_bu
BEFORE INSERT OR UPDATE
ON customer
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
-- If the new name contains a digit, strip it.
if regexp_like(:new.name, '\d') then
:new.name := regexp_replace(:new.name, '\d', NULL);
end if;
END customer_bu;
/

Oracle APEX Database Trigger - Problems with referencing database columns

I have a colon delimited list of values being stored in a varchar2 column, ORDER_PARTS_LIST, of my Oracle database.
(I understand that storing data in a list like this might not be best practice but for now just ignore that fact.)
Here are the relevant table columns:
ORDER_TABLE(
ORDER_NUMBER number,
ORDER_PARTS_LIST varchar(4000))
PARTS_TABLE(
PART_NUMBER varchar(20),
ASSIGNED_ORDER_NUMBER number)
I have a conditional trigger:
create or replace trigger "ORDER_PARTS_T1"
BEFORE
insert or update or delete on "ORDER_TABLE"
for each row
begin
if :new.ORDER_PARTS_LIST LIKE '%'+PART_NUMBER+'%' then
update PARTS_TABLE set ASSIGNED_ORDER_NUMBER = :ORDER_NUMBER;
end if;
end;
When I run this trigger I get the following error:
PLS-00201: identifier 'PART_NUMBER' must be declared
What is supposed to happen is that the trigger checks which PART_NUMBERs, in PARTS_TABLE, are included in the ORDER_PARTS_LIST, in the ORDER_TABLE, and then inserts the ORDER_NUMBER, for the affected row in ORDER_TABLE, into the ASSIGNED_ORDER_NUMBER column, of PARTS_TABLE.
In the end, all the PARTS in an ORDER should be flagged with that ORDER's NUMBER.
Does that make ANY sense???
I am not certain exactly how to properly define the variables in this trigger so that it runs and honestly I have a few doubts as to whether or not the trigger would do what I think it should even if those worked. ANY suggestions or help in getting the trigger functioing like I have defined it should would be great. Thanks in advance.
You can do string matching to test each row:
create or replace trigger "ORDER_PARTS_T1"
BEFORE
insert or update on "ORDER_TABLE"
for each row
begin
update PARTS_TABLE p
set p.ASSIGNED_ORDER_NUMBER = :new.ORDER_NUMBER
where instr(':' || :new.ORDER_PARTS_LIST || ':'
,':' || p.PART_NUMBER || ':') > 0;
end;
So for example, if ORDER_PARTS_LIST is '123:456:789', the INSTR will find matches for the ids 123, 456 and 789, but not 124, 45 or 8, for example.
When parts are removed from an order you will need a different trigger to NULL the appropriate fields in PARTS_TABLE:
create or replace trigger "ORDER_PARTS_T1"
BEFORE
update on "ORDER_TABLE"
for each row
begin
update PARTS_TABLE p
set p.ASSIGNED_ORDER_NUMBER = NULL
where instr(':' || :new.ORDER_PARTS_LIST || ':'
,':' || p.PART_NUMBER || ':') = 0
and instr(':' || :old.ORDER_PARTS_LIST || ':'
,':' || p.PART_NUMBER || ':') > 0;
end;
You are creating a trigger on the ORDER_TABLE. Since the ORDER_TABLE does not contain a column named PART_NUMBER, Oracle is unable to find the identifier 'PART_NUMBER' as it belongs to the PARTS_TABLE.
You will need to write a separate query in your trigger to access the PART_NUMBER in PARTS_TABLE.
Not entirely sure how this all fits together (seems like this won't account for the same part on multiple orders), but it looks like what you're trying to do is something like this:
create or replace trigger "ORDER_PARTS_T1"
BEFORE
insert or update or delete on "ORDER_TABLE"
for each row
begin
update parts_table
set assigned_order_number = :new.ORDER_NUMBER
where part_number in (:new.order_parts_list);
end;

SQL/Oracle 10g - Troubles with triggers/getting values from a table

I'm currently having an issue with a trigger I'm writing. I want to do a simple trigger in which after an update to table STATEMENT with the status field set to 'Sent', it would create a new row in the table NOTICE with fields such as id, date, user and the last field being a message which takes certain field values to create a "notice".
If it will help, my STATEMENT table contains the following fields:
id
List item
Title
Others not needed to know
So, with the last field of the NOTICE to be inserted, I want to create like a message, perhaps saying "The statement, (id) - (title), issued on (date) has been sent."
I currently have at the moment:
create trigger send_notice
after update on STATEMENT
for each row
when (new.status = 'Sent')
begin
insert into NOTICE values (notice_seq.nextval, SYSDATE, '10001', 'the notice
im having trouble constructing');
end send_notice;
I have tested this trigger in a database and everything seems to work fine. Another thing I was just wondering is if the formatting or if there is anything missing that might help with this trigger? And also, I would I go about creating that notice, which takes field values from STATEMENT?
Any help is appreciated
You can refer to new STATEMENT column values in the trigger using :new., and concatenate them into your text:
create trigger send_notice
after update on STATEMENT
for each row
when (new.status = 'Sent')
begin
insert into NOTICE values (notice_seq.nextval, SYSDATE, '10001',
'The statement, ' || :new.id || ' - ' || :new.title || ', issued on '
|| :new.issue_date || ' has been sent');
end send_notice;
Sometimes concatenating a lot of text and values can get confusing, and you may find it easier to use this "template" approach:
create trigger send_notice
after update on STATEMENT
for each row
when (new.status = 'Sent')
declare
l_text varchar2(500);
begin
l_text := 'The statement, #ID# - #TITLE#, issued on #DATE# has been sent';
l_text := replace (l_text, '#ID#', :new.id);
l_text := replace (l_text, '#TITLE#', :new.title);
l_text := replace (l_text, '#DATE#', :new.issue_date);
insert into NOTICE values (notice_seq.nextval, SYSDATE, '10001', l_text);
end send_notice;