Error: numeric or value errors - sql

I know this error issue was been addressed before, but I can't seem to find any relevant solution so I'm posting this question.
create table subscribers(
num_s number(6,0) ,
name varchar2(30) constraint nameM not null,
surname varchar2(20),
town varchar2(30),
age number(3,0) ,
rate number(3,0) ,
reduc number(3,0) ,
CONSTRAINT subscriber_pk primary key (num_s),
constraint age_c check (age between 0 and 120)
);
create or replace type copy_bookT as object(
num number(6),
loancode varchar2 (10),
book_ref ref bookT
);
create table copy_books of copy_bookT(
constraint pk_cb primary key (num),
constraint chk_st check (loancode in('Loan', 'Not')),
loancode default 'Loan' not null
);
create table Lending(
cb_num number(6),
sb_num number(6),
date_L date,
constraint fk_cb foreign key (cb_num) references copy_books(num),
constraint fk_sb foreign key (sb_num) references Subscribers(num_s)
);
create or replace trigger chk_DateL
for insert or update on lending
COMPOUND TRIGGER
--declare
L_Date int;
avail varchar2(10);
subtype copy_booksRec is lending%ROWTYPE;
type copied_bks is table of copy_booksRec;
cbks copied_bks := copied_bks();
before each row is
begin
cbks.extend;
cbks(cbks.last).cb_num := :new.cb_num;
cbks(cbks.last).sb_num := :new.sb_num;
end before each row;
before statement is
begin
for i in cbks.first .. cbks.last loop
select loancode into avail from copy_books where num = cbks(i).cb_num;
select count(date_L) into L_Date from lending where sb_num = cbks(i).sb_num and date_L = cbks(i).date_L;
if (L_Date = 0 and avail = 'Loan') then
update copy_books set loancode = 'Not' where num = cbks(i).cb_num;
cbks.delete;
-- cbks(i).date_L := cbks(i).date_L;
else
dbms_output.put_line('You can only make ONE LOAN at a time! You have already loaned a book on ' || L_Date);
cbks.delete;
end if;
end loop;
-- FORALL i IN cbks.first .. cbks.last
-- insert into lending values cbks(i);
cbks.delete;
end before statement;
end chk_DateL;
/
show errors
It all compiles successfully, but when I try to insert a sample record like:
insert into lending values (2, 700, '10-MAR-14');
it raises a numeric error which comes from the trigger line 18. I don't know what needs fixing despite my efforts.

You should not count on Oracle's default date format to translate your string literal to a date value , you should define the format you're using explicitly:
insert into lending values (2, 700, to_date('10-MAR-14', 'DD-MON-YY'));

While the date format issue is a valid point, that isn't causing your error. It's coming from line 18, which is the for ... loop line:
before statement is
begin
for i in cbks.first .. cbks.last loop
You've got cbks being extended and populated from the before row part of the trigger. When the before statement part fires, cbks is empty, as the row-level trigger hasn't fired yet. It's the first and last references that are throwing the ORA-06502: PL/SQL: numeric or value error error.
You can demonstrate the same thing with a simple anonymous block:
declare
type my_type is table of dual%rowtype;
my_tab my_type := my_type();
begin
for i in my_tab.first .. my_tab.last loop
null;
end loop;
end;
/
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 5
SQL Fiddle; you can see you can avoid it by adding an extend, but that doesn't really help you in your version, since you seem to want the row values. (You can eliminate the error in your code with an extend, but it's unlikely to do what you want still).
I'm really not sure what you're trying to achieve here, so I don't really have any advice on what you need to do differently.

as Mureinik already told, Oracle does not know about how to transform your varchar2 into date datatype and you should use date explicitly. But instead of making to_date use date literal - in my opinion it is more clear than using of to_date function
insert into lending values (2,700,date '2014-03-10');
by the way, you can simply change your NLS settings by altering the current session and installing the date format you need

Related

Enabling and disabling a trigger inside another trigger

I got a table Location
CREATE TABLE Location (
idL INTEGER,
City VARCHAR2(15) NOT NULL,
Street VARCHAR2(35) NOT NULL,
Nation CHAR(6) NOT NULL,
CONSTRAINT PK_idL PRIMARY KEY(idL)
);
And a table Person
CREATE TABLE Person(
p_Name VARCHAR2(20) NOT NULL,
p_Surname VARCHAR2(20) NOT NULL,
idP INTEGER,
b_Date DATE NOT NULL,
id_PL INTEGER,
CONSTRAINT PK_idP PRIMARY KEY(idP),
CONSTRAINT FK_idPL FOREIGN KEY(id_PL) REFERENCES Location(idL)
);
I calculate the primary key "automatically" as it follows:
CREATE SEQUENCE seq_loc_pk
start with 1
increment by 1;
CREATE OR REPLACE TRIGGER auto_pk_loc
BEFORE INSERT ON Location
FOR EACH ROW
BEGIN
:new.idL := seq_loc_pk.nextval;
END;
/
Now I want to insert the residence for a new person (after I've created the right view of course) with an instead of trigger like this:
CREATE OR REPLACE TRIGGER newperson
INSTEAD OF INSERT ON Residence
FOR EACH ROW
DECLARE
nl Loc.idL%TYPE;
BEGIN
ALTER TRIGGER auto_pk_loc DISABLE; -- Error
nl := seq_loc_pk.nextval;
:NEW.idL := nl;
INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation);
INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,,nl);
ALTER TRIGGER auto_pk_loc ENABLE;
END;
/
I thought about disabling and enabling the trigger auto_pk_loc so that it doesn't create extra values for no reason, but I think this is not the right way to do it? What is it though? Thanks for whoever answers.
You can do this by placing it in execute immedaite:
BEGIN
execute immedidate 'ALTER TRIGGER auto_pk_loc DISABLE';
nl := seq_loc_pk.nextval;
:NEW.idL := nl;
INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation);
INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,,nl);
execute immedidate 'ALTER TRIGGER auto_pk_loc ENABLE';
END;
/
But this will cause you all sorts of issues; DDL commits so you'll have to make this an autonomous transaction and you'll hit concurrency problems. This is best avoided.
A better method is to use the returning clause to fetch the value you just inserted:
BEGIN
INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation)
returning idl into nl;
INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,nl);
END;
/
Though as #astentx noted, you probably want to use merge to avoid having duplicate locations. This doesn't support returing, so you'll have to use some combination of insert+update instead.
Finally - assuming you're on 12c or higher - it's better to use an identity column or sequence default to auto-generate the location IDs over a trigger.

Is it possible to create a cross relationship constraint in postgresql? [duplicate]

I would like to add a constraint that will check values from related table.
I have 3 tables:
CREATE TABLE somethink_usr_rel (
user_id BIGINT NOT NULL,
stomethink_id BIGINT NOT NULL
);
CREATE TABLE usr (
id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role (
id BIGINT NOT NULL,
type BIGINT NOT NULL
);
(If you want me to put constraint with FK let me know.)
I want to add a constraint to somethink_usr_rel that checks type in role ("two tables away"), e.g.:
ALTER TABLE somethink_usr_rel
ADD CONSTRAINT CH_sm_usr_type_check
CHECK (usr.role.type = 'SOME_ENUM');
I tried to do this with JOINs but didn't succeed. Any idea how to achieve it?
CHECK constraints cannot currently reference other tables. The manual:
Currently, CHECK expressions cannot contain subqueries nor refer to
variables other than columns of the current row.
One way is to use a trigger like demonstrated by #Wolph.
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints, which are the first choice to enforce referential integrity. Related answer on dba.SE with detailed instructions:
Enforcing constraints “two tables away”
Another option would be to "fake" an IMMUTABLE function doing the check and use that in a CHECK constraint. Postgres will allow this, but be aware of possible caveats. Best make that a NOT VALID constraint. See:
Disable all constraints and table checks while restoring a dump
A CHECK constraint is not an option if you need joins. You can create a trigger which raises an error instead.
Have a look at this example: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
...i did it so (nazwa=user name, firma = company name) :
CREATE TABLE users
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
nazwa character varying(20),
firma character varying(50)
);
CREATE TABLE test
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
firma character varying(50),
towar character varying(20),
nazwisko character varying(20)
);
ALTER TABLE public.test ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$
declare
result varchar(50);
BEGIN
select into result users.firma from users where users.nazwa = current_user;
return result;
END;
$$ LANGUAGE plpgsql;
CREATE POLICY user_policy ON public.test
USING (firma = whoIAM3());
CREATE FUNCTION test_trigger_function()
RETURNS trigger AS $$
BEGIN
NEW.firma:=whoIam3();
return NEW;
END
$$ LANGUAGE 'plpgsql'
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();

Sql trigger checks if age is more then 16

Using Oracle database , I have created a table :
CREATE TABLE PLAYER (
PID number NOT NULL,
FIRSTNAME varchar2(100) NOT NULL,
SURNAME varchar2(100) NULL,
DATEOFBIRTH date NULL,
EXPERIENCE integer NULL,
CONSTRAINT PLAYER_PK PRIMARY KEY (PID)
) ;
I would like to make a trigger which will checks the age of player if is more then 16. I have this trigger, it does not work.
CREATE OR REPLACE TRIGGER t_DATE_BIRTH
BEFORE INSERT ON PLAYER
FOR EACH ROW
BEGIN
if (extract(year from (sysdate-:new.DATEOFBIRTH) year to month )<16) then
dbms_output.put_line ('Player must be above 16 years old ');
rollback;
end if
END;
/
Maybe because I have changed format of the date using
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYY';
Any suggestions?
The format of the date is irrelevant. Instead, raise an error:
CREATE OR REPLACE TRIGGER t_DATE_BIRTH
BEFORE INSERT ON PLAYER
FOR EACH ROW
BEGIN
if (extract(year from (sysdate-:new.DATEOFBIRTH) year to month ) < 16) then
RAISE_APPLICATION_ERROR -20002, 'Player must be above 16 years old');
end if;
END;
In general, a better way to check for age is not to just use the year. I would recommend:
if add_months(:new.DateOfBirth, 16*12) >= sysdate then
. . .

Oracle SQL - PLS-00049 - bad bind variable

I have table defined like this:
create table "nakup" (
"cislo_n" INTEGER not null,
"id_zak" INTEGER not null,
"jm_pobocky" CHAR(15) not null,
"datum_cas" DATE not null
constraint CKC_DATUM_CAS_NAKUP check ("datum_cas" >= TO_DATE('1.01.1994 8:30:25', 'DD.MM.YYYY HH24:MI:SS')),
constraint PK_NAKUP primary key ("cislo_n")
I want to create a trigger that would block inserting a date from the future, my code looks like this:
create or replace TRIGGER TRIGGER1
BEFORE INSERT OR UPDATE ON "nakup"
FOR EACH ROW
BEGIN
if (:new.datum_cas > current_timestamp) then
raise_application_error(-20000, 'Špatně zadané datum a čas.');
end if;
END;
I keep getting error Error(5,7): PLS-00049: chybná vázaná proměnná 'NEW.DATUM_CAS' (bad bind variable in english). What am I doing wrong?
As Gordon Linoff suggested, your trigger will compile if you enclose the column name in double quotes:
create or replace TRIGGER TRIGGER1
BEFORE INSERT OR UPDATE ON "nakup"
FOR EACH ROW
BEGIN
if (:new."datum_cas" > current_timestamp) then
raise_application_error(-20000, 'Špatně zadané datum a čas.');
end if;
END;
/
Trigger TRIGGER1 compiled
Quoted identifiers have to be quoted everywhere they are referenced. You seem to have realised that when you referred to the table name in the trigger definition, but it applies to the column names too.
Your life will be much simpler if you use unquoted identifiers (or quoted uppercase identifiers, which is the same thing as long as they don't contain any invalid characters). Oracle does not recommend using quoted identifiers for database object names. So this works with no double-quotes at all:
create table nakup (
cislo_n INTEGER not null,
id_zak INTEGER not null,
jm_pobocky CHAR(15) not null,
datum_cas DATE not null,
constraint CKC_DATUM_CAS_NAKUP
check (datum_cas >= TO_DATE('1.01.1994 8:30:25', 'DD.MM.YYYY HH24:MI:SS')),
constraint PK_NAKUP primary key (cislo_n)
);
Table NAKUP created.
create or replace TRIGGER TRIGGER1
BEFORE INSERT OR UPDATE ON nakup
FOR EACH ROW
BEGIN
if (:new.datum_cas > current_timestamp) then
raise_application_error(-20000, 'Špatně zadané datum a čas.');
end if;
END;
/
Trigger TRIGGER1 compiled
You can then refer to nakup.datum_cas etc. in your code, instead of having to use "nakup"."datum_cas".
yes...
datum_cas column name is different from datum_cas column name..
Oracle stores column names as uppercase by default,if we use double quotes ("") the column name is stored as-is (upper/lower)
example....
SQL> create table test5 (id number,"id1" number);
table created.
SQL> insert into test5 values(1,2);
1 row created.
SQL> select * from test5;
ID id1
----- ----------
1 2
id is stored as ID and id1 is stored as id1.

SQL number checking trigger

I need ID to be an eight whole digit number.
create table foo(
ID primary key DEFERRABLE,
);
create or replace trigger foo_trg
before insert or update
on foo
for each row
Begin
if :new.ID > 99999999 or :new.ID < 9999999 then
raise pkg.Illegal_update;
end if;
end;
/
Right now my trigger can only stop an illegal update if the number is too large or too small. I need a way of checking for a decimal in a number
Any suggestions are welcome thank you
What's the DBMS? I'd declare the type of the id column to be an integer. In Oracle, you'd do it like this:
create table foo(
ID NUMBER(8,0) primary key DEFERRABLE,
);
Is there some reason this is made deferrable?
You can use floor(number) = number if all else fails.