This question already has answers here:
What exactly do quotation marks around the table name do?
(2 answers)
Closed 5 years ago.
I'm trying to create a trigger in Oracle, but it's returning the error
ORA-00942: table or view does not exist
Here is the table:
CREATE TABLE quartos(
idQuarto NUMBER(11),
numeroQ NUMBER(11),
limitePessoas NUMBER(2),
valorDiaria NUMBER(10,2),
situacao NUMBER(1), CONSTRAINT idQuarto_pk PRIMARY KEY (idQuarto)
);
Here is the sequence:
CREATE sequence "quartos_seq";
And here is the trigger:
CREATE trigger "bi_quartos"
before insert on "quartos"
for each row
begin
select "quartos_seq".nextval into :NEW."idQuarto" from dual;
end;
I've creatend another trigger before the same way and nothing went wrong. I just changed the parameters and now its returning that error
remove the double quotes from
CREATE trigger "bi_quartos"
before insert on "quartos"
Tables names are converted to upper case. What you are doing is forcing it to search a table with lower case which doesn't exist.
You're mixing case sensitive and case insensitive identifiers for your table and column names.
If you don't wrap the original declarations in double-quotes, the are created as case insensitive and you can't use double-quotes when you try and use them:
CREATE or replace trigger "bi_quartos"
before insert on quartos
for each row
begin
select "quartos_seq".nextval into :new.idQuarto from dual;
end;
Related
Given this table:
create table test (
name text primary key
);
I need to write a plpgsql function with a variable name that collides with the primary key name, which I must use in a on conflict clause:
create or replace function func(
name text -- this variable name...
) returns void language plpgsql as
$$
begin
insert into test (name) values (name)
on conflict (name) do update -- ...conflicts with this line
set name = func.name;
end;
$$;
This compiles, but then throws an ambiguous column reference:
select * from func('one');
ERROR: column reference "name" is ambiguous
LINE 2: on conflict (name) do update
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
QUERY: insert into test (name) values (name)
on conflict (name) do update
set name = func.name
CONTEXT: PL/pgSQL function func(text) line 3 at SQL statement
I tried specifying the full column name as on conflict (test.name) which does not compile, or ((test.name)) which compiles:
create or replace function func(
name text
) returns void language plpgsql as
$$
begin
insert into test (name) values (name)
on conflict ((test.name)) do -- this fails too
update set name = func.name;
end;
$$;
But it fails as well:
select * from func('two');
ERROR: invalid reference to FROM-clause entry for table "test"
LINE 2: on conflict ((test.name)) do
^
HINT: There is an entry for table "test", but it cannot be referenced from this part of the query.
QUERY: insert into test (name) values (name)
on conflict ((test.name)) do
update set name = func.name
CONTEXT: PL/pgSQL function func(text) line 3 at SQL statement
Is there a solution?
Edit: I found a workaround:
on conflict on constraint test_pkey do update
where test_pkey is the table name plus _pkey. I don't know how reliable this is though. I'd still like to specify the column name instead.
to start with, name is a bad name for both variable and attribute. When you have both, code won't look good. with that in mind, you can "prefix" variable with labeled block (in example below <<fn>>``), and setvariable_conflict` to give preference to column name, see code below:
t=# create or replace function func(
name text
) returns void language plpgsql as
$$
#variable_conflict use_column
<<fn>>
declare name text :='blah';
begin
insert into test (name) values (name)
on conflict (name) do -- this no longer fails
update set name = fn.name;
end;
$$;
t=# insert into test select 'b';
INSERT 0 1
Time: 8.076 ms
t=# select func('b');
func
------
(1 row)
Time: 6.117 ms
t=# select * from test;
name
------
b
blah
(2 rows)
https://www.postgresql.org/docs/current/static/plpgsql-implementation.html#PLPGSQL-VAR-SUBST
By default, PL/pgSQL will report an error if a name in a SQL statement
could refer to either a variable or a table column. You can fix such a
problem by renaming the variable or column, or by qualifying the
ambiguous reference, or by telling PL/pgSQL which interpretation to
prefer.
and further - basically the whole link is about it.
And yet - after demonstrating how particular task this can be easily done with plpgsql, I still quote namual:
The simplest solution is to rename the variable or column. A common
coding rule is to use a different naming convention for PL/pgSQL
variables than you use for column names. For example, if you
consistently name function variables v_something while none of your
column names start with v_, no conflicts will occur.
The ON CONFLICT... syntax (as documented here) uses a unique constraint to determine if the row conflicts. You can specify this unique constraint either by listing the columns it contains (at which point Postgres "infers" the correct index to use) or by naming the constraint directly.
In your case, the unique constraint being used is the primary key constraint implicitly created during your CREATE TABLE statement. This will have a name given to it by the DBMS, unless you specify one directly; so you will need to either look up the name the DBMS has given it (and be aware that this may change if you recreate the schema later), or name it explicitly when you create the table using the syntax CONSTRAINT pk_some_name PRIMARY KEY.
You would then specify the clause as ON CONFLICT ON CONSTRAINT pk_some_name DO ... (note no brackets around the constraint name).
(Alternatively, of course, you could change your function to use an unambiguous parameter name; personally, I think it's good practice to use a prefix like p_ or in_ rather than handling conflicts on a case-by-case basis.)
I am new to PLSQL in Oracle. When I am learning about triggers, I have read from this source https://www.techonthenet.com/oracle/triggers/before_insert.php which says that when I create a BEFORE INSERT Trigger in Oracle, the FOR EACH ROW is NOT always needed, hence the syntax is enclosed by square brackets [ ]. I have written this simple trigger:
CREATE OR REPLACE TRIGGER enroll_time
BEFORE INSERT
ON ENROLL
FOR EACH ROW
BEGIN
:new.addtime := sysdate;
END;
/
If I remove the FOR EACH ROW in the above, I actually get an error:
Error report -
ORA-04082: NEW or OLD references not allowed in table level triggers
04082. 00000 - "NEW or OLD references not allowed in table level triggers"
*Cause: The trigger is accessing "new" or "old" values in a table trigger.
*Action: Remove any new or old references.
From the error message, it seems like if I use :new.[column_name], then FOR EACH ROW must have to exist. Why is this? Is there any example that FOR EACH ROW is NOT needed in a BEFORE INSERT TRIGGER in Oracle?
Is there any example that FOR EACH ROW is NOT needed in a BEFORE INSERT TRIGGER in Oracle?
Simple example of statement level trigger:
CREATE TABLE test_table(col VARCHAR2(10));
CREATE OR REPLACE TRIGGER enroll_time
BEFORE INSERT
ON ENROLL
BEGIN
INSERT INTO test_table(col)
SELECT 1 FROM dual;
END;
/
I highly recommend to read about compound trigger to understand when each part is fired.
Basically, if you need to use :OLD or :NEW pseudotables, you need a row level trigger. An example of a statement level trigger would be inserting a record into a table when another table is effected.
Parameter table is initially created and one row is added in Postgres.
This table should have always one row, otherwise SQL queries using this table will produce incorrect results. DELETE or INSERT to this table are disallowed, only UPDATE is allowed.
How to add single row constraint to this table?
Maybe DELETE and INSERT triggers can raise an exception or is there simpler way?
The following will create a table where you can only insert one single row. Any update of the id column will result in an error, as will any insert with a different value than 42. The actual id value doesn't matter actually (unless there is some special meaning that you need).
create table singleton
(
id integer not null primary key default 42,
parameter_1 text,
parameter_2 text,
constraint only_one_row check (id = 42)
);
insert into singleton values (default);
To prevent deletes you can use a rule:
create or replace rule ignore_delete
AS on delete to singleton
do instead nothing;
You could also use a rule to make insert do nothing as well if you want to make an insert "fail" silently. Without the rule, an insert would generate an error. If you want a delete to generate an error as well, you would need to create a trigger that simply raises an exception.
Edit
If you want an error to be thrown for inserts or deletes, you need a trigger for that:
create table singleton
(
id integer not null primary key,
parameter_1 text,
parameter_2 text
);
insert into singleton (id) values (42);
create or replace function raise_error()
returns trigger
as
$body$
begin
RAISE EXCEPTION 'No changes allowed';
end;
$body$
language plpgsql;
create trigger singleton_trg
before insert or delete on singleton
for each statement execute procedure raise_error();
Note that you have to insert the single row before you create the trigger, otherwise you can't insert that row.
This will only partially work for a superuser or the owner of the table. Both have the privilege to drop or disable the trigger. But that is the nature of a superuser - he can do anything.
To make any table a singleton just add this column:
just_me bool NOT NULL DEFAULT TRUE UNIQUE CHECK (just_me)
This allows exactly one row. Plus add the trigger #a_horse provided.
But I would rather use a function instead of the table for this purpose. Simpler and cheaper.
CREATE OR REPLACE FUNCTION one_row()
RETURNS TABLE (company_id int, company text) LANGUAGE sql IMMUTABLE AS
$$SELECT 123, 'The Company'$$
ALTER FUNCTION one_row() OWNER TO postgres;
Set the owner to the user that should be allowed to change it.
Give a user permission to ALTER a function
Nobody else change it - except superusers of course. Superusers can do anything.
You can use this function just like you would use the table:
SELECT * FROM one_row();
If you need a "table", create a view (which is actually a special table internally):
CREATE VIEW one_row AS SELECT * FROM one_row();
I guess you will not use the PostgreSQL root user in your application so you could simply limit the permissions of your application user on UPDATE for this table.
An INSERT or DELETE will then cause an Insufficient privilege exception.
When I try to create a trigger in Oracle 11g, I get the following errors:
ORA-06552: PL/SQL: COMPILATION UNIT ANALYSIS TERMINATED
ORA-06553: PLS-320: THE DECLARATION OF THIS TYPE OF THIS EXPRESSION IS INCOMPLETE OR MALFORMED.
I've tried changing the table name from EVENT to another table and the trigger compiles but my search for reserved keywords only indicates EVENTS though. Is something else wrong with the trigger?
CREATE TRIGGER GEN_EVENT_ID
BEFORE INSERT ON EVENT
FOR EACH ROW
BEGIN
:NEW.ID := EVENT_ID_SEQ.NEXTVAL;
END;
Table
CREATE TABLE EVENT (
ID NUMBER(19,0) NOT NULL,
TIMESTAMP TIMESTAMP NOT NULL,
NAME VARCHAR2(255)
);
Id appears in the v$RESERVED_WORDS view, which is the view of SQL keywords. Check this link for more information on this.
Select *
From V$RESERVED_WORDS
Where KEYWORD = 'ID'
While Id is not marked as Reserved in this view, there seems to be some problem with using column names that appear in that list for triggers as stated in this other SO question.
If you read it you will see the OP had the same problem but with timestamp. In the other SO question a workaround was suggested that worked for the OP. If you change the column name the problem should disappear.
Update
Seeing your table declaration, all of your column names appear in the V$RESERVED_WORDS. Particularly the timestamp column is the same one mentioned in the other SO question (see link above) as being problematic for triggers.
My suggestion is to rename both the table and its columns. For example:
Create Table my_EVENT (
event_Id Number(19,0) Not Null,
event_Timestamp Timestamp Not Null,
event_Name Varchar2(255)
);
I have a workqueue table that has a workid column. The workID column has values that increment automatically. Is there a way I can run a query in the backend to insert a new row and have the workID column increment automatically?
When I try to insert a null, it throws error ORA01400 - Cannot insert null into workid.
insert into WORKQUEUE (facilitycode,workaction,description) values ('J', 'II', 'TESTVALUES')
What I have tried so far - I tried to look at the table details and didn't see any auto-increment. The table script is as follow
"WORKID" NUMBER NOT NULL ENABLE,
Database: Oracle 10g
Screenshot of some existing data.
ANSWER:
I have to thank each and everyone for the help. Today was a great learning experience and without your support, I couldn't have done. Bottom line is, I was trying to insert a row into a table that already has sequences and triggers. All I had to do was find the right sequence, for my question, and call that sequence into my query.
The links you all provided me helped me look these sequences up and find the one that is for this workid column. Thanks to you all, I gave everyone a thumbs up, I am able to tackle another dragon today and help patient care take a step forward!"
This is a simple way to do it without any triggers or sequences:
insert into WORKQUEUE (ID, facilitycode, workaction, description)
values ((select max(ID)+1 from WORKQUEUE), 'J', 'II', 'TESTVALUES')
It worked for me but would not work with an empty table, I guess.
To get an auto increment number you need to use a sequence in Oracle.
(See here and here).
CREATE SEQUENCE my_seq;
SELECT my_seq.NEXTVAL FROM DUAL; -- to get the next value
-- use in a trigger for your table demo
CREATE OR REPLACE TRIGGER demo_increment
BEFORE INSERT ON demo
FOR EACH ROW
BEGIN
SELECT my_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
/
There is no built-in auto_increment in Oracle.
You need to use sequences and triggers.
Read here how to do it right. (Step-by-step how-to for "Creating auto-increment columns in Oracle")
ELXAN#DB1> create table cedvel(id integer,ad varchar2(15));
Table created.
ELXAN#DB1> alter table cedvel add constraint pk_ad primary key(id);
Table altered.
ELXAN#DB1> create sequence test_seq start with 1 increment by 1;
Sequence created.
ELXAN#DB1> create or replace trigger ad_insert
before insert on cedvel
REFERENCING NEW AS NEW OLD AS OLD
for each row
begin
select test_seq.nextval into :new.id from dual;
end;
/ 2 3 4 5 6 7 8
Trigger created.
ELXAN#DB1> insert into cedvel (ad) values ('nese');
1 row created.
You can use either SEQUENCE or TRIGGER to increment automatically the value of a given column in your database table however the use of TRIGGERS would be more appropriate. See the following documentation of Oracle that contains major clauses used with triggers with suitable examples.
Use the CREATE TRIGGER statement to create and enable a database trigger, which is:
A stored PL/SQL block associated with a table, a schema, or the
database or
An anonymous PL/SQL block or a call to a procedure implemented in
PL/SQL or Java
Oracle Database automatically executes a trigger when specified conditions occur. See.
Following is a simple TRIGGER just as an example for you that inserts the primary key value in a specified table based on the maximum value of that column. You can modify the schema name, table name etc and use it. Just give it a try.
/*Create a database trigger that generates automatically primary key values on the CITY table using the max function.*/
CREATE OR REPLACE TRIGGER PROJECT.PK_MAX_TRIGGER_CITY
BEFORE INSERT ON PROJECT.CITY
FOR EACH ROW
DECLARE
CNT NUMBER;
PKV CITY.CITY_ID%TYPE;
NO NUMBER;
BEGIN
SELECT COUNT(*)INTO CNT FROM CITY;
IF CNT=0 THEN
PKV:='CT0001';
ELSE
SELECT 'CT'||LPAD(MAX(TO_NUMBER(SUBSTR(CITY_ID,3,LENGTH(CITY_ID)))+1),4,'0') INTO PKV
FROM CITY;
END IF;
:NEW.CITY_ID:=PKV;
END;
Would automatically generates values such as CT0001, CT0002, CT0002 and so on and inserts into the given column of the specified table.
SQL trigger for automatic date generation in oracle table:
CREATE OR REPLACE TRIGGER name_of_trigger
BEFORE INSERT
ON table_name
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT sysdate INTO :NEW.column_name FROM dual;
END;
/
the complete know how, i have included a example of the triggers and sequence
create table temasforo(
idtemasforo NUMBER(5) PRIMARY KEY,
autor VARCHAR2(50) NOT NULL,
fecha DATE DEFAULT (sysdate),
asunto LONG );
create sequence temasforo_seq
start with 1
increment by 1
nomaxvalue;
create or replace
trigger temasforo_trigger
before insert on temasforo
referencing OLD as old NEW as new
for each row
begin
:new.idtemasforo:=temasforo_seq.nextval;
end;
reference:
http://thenullpointerexceptionx.blogspot.mx/2013/06/llaves-primarias-auto-incrementales-en.html
For completeness, I'll mention that Oracle 12c does support this feature. Also it's supposedly faster than the triggers approach. For example:
CREATE TABLE foo
(
id NUMBER GENERATED BY DEFAULT AS IDENTITY (
START WITH 1 NOCACHE ORDER ) NOT NULL ,
name VARCHAR2 (50)
)
LOGGING ;
ALTER TABLE foo ADD CONSTRAINT foo_PK PRIMARY KEY ( id ) ;
Best approach: Get the next value from sequence
The nicest approach is getting the NEXTVAL from the SEQUENCE "associated" with the table. Since the sequence is not directly associated to any specific table,
we will need to manually refer the corresponding table from the sequence name convention.
The sequence name used on a table, if follow the sequence naming convention, will mention the table name inside its name. Something likes <table_name>_SEQ. You will immediately recognize it the moment you see it.
First, check within Oracle system if there is any sequence "associated" to the table
SELECT * FROM all_sequences
WHERE SEQUENCE_OWNER = '<schema_name>';
will present something like this
Grab that SEQUENCE_NAME and evaluate the NEXTVAL of it in your INSERT query
INSERT INTO workqueue(id, value) VALUES (workqueue_seq.NEXTVAL, 'A new value...')
Additional tip
In case you're unsure if this sequence is actually associated with the table, just quickly compare the LAST_NUMBER of the sequence (meaning the current value) with the maximum id of
that table. It's expected that the LAST_NUMBER is greater than or equals to the current maximum id value in the table, as long as the gap is not too suspiciously large.
SELECT LAST_NUMBER
FROM all_sequences
WHERE SEQUENCE_OWNER = '<schema_name>' AND SEQUENCE_NAME = 'workqueue_seq';
SELECT MAX(ID)
FROM workqueue;
Reference: Oracle CURRVAL and NEXTVAL
Alternative approach: Get the current max id from the table
The alternative approach is getting the max value from the table, please refer to Zsolt Sky answer in this same question
This is a simple way to do it without any triggers or sequences:
insert into WORKQUEUE (ID, facilitycode, workaction, description)
values ((select count(1)+1 from WORKQUEUE), 'J', 'II', 'TESTVALUES');
Note : here need to use count(1) in place of max(id) column
It perfectly works for an empty table also.