sql - insert if not exists - sql

I am having trouble with a sql query. I need to insert a row if the same row does not exist already. This is what I have so far:
DECLARE
BEGIN
FOR FOLDER_ROW IN (SELECT FOLDERID, USERID FROM DATA1.FOLDERS)
LOOP
IF NOT EXISTS (SELECT * FROM DATA1.FOLDER_USER WHERE FOLDER_ID = FOLDER_ROW.FOLDERID AND USER_ID = FOLDER_ROW.USERID)
INSERT INTO DATA1.FOLDER_USER (FOLDER_ID, USER_ID) VALUES (FOLDER_ROW.FOLDERID, FOLDER_ROW.USERID);
END LOOP;
COMMIT;
END;
I would not be very familiar with sql particularly the not exists syntax so when I execute I get the following error:
ORA-06550: line 37, column 11: PLS-00103: Encountered the symbol
"INSERT" when expecting one of the following:
then and or
The symbol "then" was substituted for "INSERT" to continue.
ORA-06550: line 38, column 10:
PLS-00103: Encountered the symbol "LOOP" when expecting one of the following:
if
ORA-06550: line 40, column 5:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
end not pragma final instantiable order overriding static
member constructor map

Do it all in SQL rather than context switching into PL/SQL:
INSERT INTO DATA1.FOLDERS
(folder_id,
user_id)
SELECT f1.folder_id,
f1.user_id
FROM DATA1.FOLDERS f1
WHERE NOT EXISTS (SELECT 1
FROM DATA1.FOLDERS f2
WHERE f1.folder_id = f2.folder_id
AND f1.user_id = f2.user_id);

A better (Oracle specific?) solution may be the MERGE statement.
See here for a nice explanation, with examples:
http://www.oracle-base.com/articles/10g/MergeEnhancements10g.php
Hope that helps.

You forgot the THEN
IF condition THEN
...
ELSE
...
END IF;

DECLARE
N_COUNTS NUMBER;
BEGIN
select count(ID) into N_COUNTS from TABLE_NAME where ID = 1;
IF N_COUNTS=0 THEN
INSERT QUERY....
ELSE
UPDATE QUERY....
END IF;
END;

Needed Similar for Sqlite
I need to insert into a sqlite db but only if the key doesn't already exist.
id is the primary key autoincremented.
key is a unique string.
MainToken Table
id | key | created | active
1 | test | 2022-01-07 | 1
2 | 2ndOne | 2021-12-19 | 1
Finally, if the item already exists then I want that id but if it was just inserted then I want that new id.
Query That Works -- based on answer
insert into maintoken (key)
select $key
where not exists
(select key from maintoken where key=$key);
select id from maintoken where key=$key and active=1;
This will insert the $key value into the maintoken table
only if the $key doesn't already exist.
After the new unique key is inserted it will select and get the new ID value and return it.
If the new key isn't unique it will return the value of that existing key's id.

Related

Trigger to automatically add tuples to relation

I'm trying to create a trigger on SQLITE to automatically add tuples to another table when something is added to a certain table, but firstly, I want to check if what I'm adding belongs to another third table.
This is my trigger:
DROP TRIGGER IF EXISTS atualizaBibliotecas;
CREATE TRIGGER atualizaBibliotecas
AFTER INSERT ON FoiComprado
FOR EACH ROW
BEGIN
SELECT CASE
WHEN
(
SELECT count(*)
FROM Album
WHERE idItem = NEW.idItem
) <> 0
THEN INSERT INTO Possui(idItem, email, dataAquisicao) VALUES(11,'aaaa#bbbb.com', '2019-05-14');
END;
This is the error I'm receiving:
Error: near line 8: near "INSERT": syntax error
LINE 8:
CREATE TRIGGER atualizaBibliotecas
I believe that you want :-
CREATE TRIGGER atualizaBibliotecas
AFTER INSERT ON FoiComprado
FOR EACH ROW
WHEN
(
SELECT count(*)
FROM Album
WHERE idItem = NEW.idItem
) <> 0
BEGIN
INSERT INTO Possui(idItem, email, dataAquisicao) VALUES(11,'aaaa#bbbb.com', '2019-05-14');
END;
SQLite doesn't support control-flow logic in its triggers. You can express this as a conditional insert:
INSERT INTO Possui (idItem, email, dataAquisicao)
SELECT 11, 'aaaa#bbbb.com', '2019-05-14'
WHERE NOT EXISTS (SELECT 1 FROM Album a WHERE a.idItem = NEW.idItem);
I find it surprising that you are hardcoding 11 for the insert. I would expect NEW.idITEM.

Trigger with lookup from another table

I'm learning PL/SQL specifically triggers, and I want to learn how to operate on multiple tables using triggers but I'm having trouble understanding the process. Here's an example:
CREATE TABLE Sess
(code INTEGER NOT NULL,
dateStart DATE NOT NULL,
dateEnd DATE NOT NULL,
CONSTRAINT KeySess PRIMARY KEY(code)
)
CREATE TABLE Ins
(codeIns CHAR(12) NOT NULL,
code INTEGER NOT NULL,
dateIns DATE NOT NULL,
dateAb DATE,
note INTEGER,
CONSTRAINT KeyIns PRIMARY KEY (codeIns, code)
)
1/ I'm trying to build a trigger that will ensure this:
before Insert or Update Ins
dateAb is between dateStart and dateEnd
2/ I want also to ALTER TABLE and lock code, and codeIns in Ins so no one can update them but i don't know the command.
For the first trigger, I tried something like:
CREATE OR REPLACE TRIGGER test
BEFORE INSERT OR UPDATE OF dateAb ON Ins
FOR EACH ROW
DECLARE
start date;
BEGIN
SELECT DISTINCT s.dateStart INTO start
WHERE Ins.code = s.code
IF (:NEW.dateAb < start) THEN
raise_application_error(-20101,'dateAb incorrect');
END IF;
END;
Note: I didn't declare a variable end to check if it's between, my purpose here was to test the correct syntax
Note2: the table are empty.
I had Warning: Trigger created with compilation errors. and 3 errors :
PL/SQL: SQL Statement ignored
4/40 PL/SQL: ORA-00923: key-word FROM absent
9/5 PLS-00103: Encountered the symbol "IF" when expecting one of the
following:
;
And finally like I said I don't know the correct syntax to lock column on table to prevent updates or if it's possible.
I'm learning so if one of you can teach me the correct way to process with my two request, I would apreciate that.
Thank you
Try this:
create or replace trigger test
before insert or update of dateab on ins
for each row
declare
l_sess_start date;
begin
select s.datestart into l_sess_start
from sess s
where s.code = :new.code;
if :new.dateab < l_sess_start then
raise_application_error
( -20101
, 'Start date ' || to_char(:new.dateab,'DD-MON-YYYY') ||
' cannot be before ' || to_char(l_sess_start,'DD-MON-YYYY'));
end if;
end;
Test:
insert into sess values (1, date '2018-04-22', date '2018-04-30');
insert into ins values ('Z', 1, date '2018-04-01', date '2018-04-01', null);
ERROR at line 1:
ORA-20101: Start date 01-APR-2018 cannot be before 22-APR-2018
ORA-06512: at "WILLIAM.TEST", line 10
ORA-04088: error during execution of trigger 'WILLIAM.TEST'
PLS-00103: Encountered the symbol "IF" when expecting one of the
following:
;
This error is because you are missing ; after select statement

Trigger in Oracle giving error

This is my sql code:
CREATE OR REPLACE TRIGGER PLACE_NO_TRIGGER
BEFORE INSERT or UPDATE ON UHA_LEASE
FOR EACH ROW BEGIN
INSERT INTO UHA_TEMPVAL SELECT PLACE_NO FROM UHA_RESHALL;
INSERT INTO UHA_TEMPVAL SELECT PLACE_NO FROM UHA_GENERAL;
IF(:NEW.PLACE_NO NOT IN UHA_TEMPVAL.TEMP_VALUE AND :NEW.PLACE_NO IS NOT NULL) THEN
RAISE_APPLICATION_ERROR(-10001, 'PLACE_NO MUST BE IN UHA_RESHALL OR IN UHA_GENERAL OR NULL');
END IF;
DELETE FROM UHA_TEMPVAL;
END;
This is giving these errors:
Error(4,27): PLS-00103: Encountered the symbol "UHA_TEMPVAL" when expecting one of the following: ( The symbol "(" was substituted for "UHA_TEMPVAL" to continue.
Error(4,69): PLS-00103: Encountered the symbol "THEN" when expecting one of the following: ) , and or as The symbol ")" was substituted for "THEN" to continue.
Can someone help me understand why these errors are occurring? It's especially odd because row 4 ends at column 60.
Thanks!
It looks like you're trying to enforce referential integrity from a child table to either of two parent tables. You don't really need to insert and delete from a temporary table; you can count how many rows match in either table:
CREATE OR REPLACE TRIGGER PLACE_NO_TRIGGER
BEFORE INSERT or UPDATE ON UHA_LEASE
FOR EACH ROW
DECLARE
CNT NUMBER;
BEGIN
SELECT COUNT(*)
INTO CNT
FROM (
SELECT PLACE_NO FROM UHA_RESHALL
WHERE PLACE_NO = :NEW.PLACE_NO
UNION ALL
SELECT PLACE_NO FROM UHA_GENERAL
WHERE PLACE_NO = :NEW.PLACE_NO
);
IF :NEW.PLACE_NO IS NOT NULL AND CNT = 0 THEN
RAISE_APPLICATION_ERROR(-10001,
'PLACE_NO MUST BE IN UHA_RESHALL OR IN UHA_GENERAL OR NULL');
END IF;
END;
/
You could choose to only run the query if the :NEW.PLACE_NO is not null but it probably won't make much difference if the columns are indexed.

Unique constraint on one column with excluding row with same values in other

I'd like to add a unique key to column value but I must ignore rows that have the same values in columns value and header_id. For example, consider this table:
id | header_id | value
1 | 1 | a
2 | 1 | a
3 | 2 | a
So rows 1 and 2 point to same object and the unique key should accept them, but row 3 has a different header_id (pointing to another object) and, because it has the same value as object 1, it should violate unique constraint and raise an error.
Edit 16.2:1327:
I'm using a core framework that generates columns to handle history so I cannot normalize the table. My class has lots of columns but for this example I'm only considering the value column.
You could do it if you can change your table structure slightly:
your_table
id header_value
1 1
2 1
3 2
header_value
id header_id value
1 1 a
2 2 a
Add a foreign key constraint from your_table.header_value to header_value.id.
Now you can add a unique constraint on header_value.value.
You could use a trigger to simulate a unique constraint with your desired properties. Something like this would do the trick:
create or replace function sort_of_unique() returns trigger as $$
declare
got_one boolean;
begin
select exists(
select 1
from your_table
where header_id != new.header_id
and value = new.value
) into got_one;
if got_one then
raise exception 'Uniqueness violation in your_table';
end if;
return new;
end;
$$ language plpgsql;
create trigger sort_of_unique_trigger
before insert or update on your_table
for each row execute procedure sort_of_unique();
Then you'd get things like this happening:
=> insert into your_table (id, header_id, value) values (1, 1, 'a');
=> insert into your_table (id, header_id, value) values (2, 1, 'a');
=> insert into your_table (id, header_id, value) values (3, 2, 'a');
ERROR: Uniqueness violation in your_table
=> insert into your_table (id, header_id, value) values (3, 2, 'b');
=> update your_table set value = 'a' where id = 3;
ERROR: Uniqueness violation in your_table
You can create partial unique indexes by attaching a WHERE clause to the index. This allows you to apply your uniqueness constraint to slices of the table; however, I can't think of a way to get the WHERE clause to specify an "anti-slice" so I don't see a way to make this work with a partial index. I could be missing something obvious though.
After a while I found something. Using constrain CHECK with function to determine if exist (Cannot use SELECT in CHECK statement but you can use function with desired select)
CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) RETURNS BOOLEAN AS
$$
BEGIN
RETURN NOT EXISTS (SELECT header_id,value FROM myschema.mytalbe WHERE value LIKE _value AND header_id != _header_id LIMIT 1);
END;
$$ LANGUAGE plpgsql;
ALTER TABLE mytable ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value))

SQL to insert only if a certain value is NOT already present in the table?

How to insert a number into the table, only if the table does not already have that number in it?
I am looking for specific SQL code, not really sure how to approach this. Tried several things, nothing's working.
EDIT
Table looks like this:
PK ID Value
1 4 500
2 9 3
So if I am trying to INSERT (ID, Value) VALUES (4,100) it should not try to do it!
If ID is supposed to be unique, there should be a unique constraint defined on the table. That will throw an error if you try to insert a value that already exists
ALTER TABLE table_name
ADD CONSTRAINT uk_id UNIQUE( id );
You can catch the error and do whatever you'd like if an attempt is made to insert a duplicate key-- anything from ignoring the error to logging and re-raising the exception to raising a custom exception
BEGIN
INSERT INTO table_name( id, value )
VALUES( 4, 100 );
EXCEPTION
WHEN dup_val_on_index
THEN
<<do something>>
END;
You can also code the INSERT so that it inserts 0 rows (you would still want the unique constraint in place both from a data model standpoint and because it gives the optimizer more information and may make future queries more efficient)
INSERT INTO table_name( id, value )
SELECT 4, 100
FROM dual
WHERE NOT EXISTS(
SELECT 1
FROM table_name
WHERE id = 4 )
Or you could code a MERGE instead so that you update the VALUE column from 500 to 100 rather than inserting a new row.
Try MERGE statement:
MERGE INTO tbl USING
(SELECT 4 id, 100 value FROM dual) data
ON (data.id = tbl.id)
WHEN NOT MATCHED THEN
INSERT (id, value) VALUES (data.id, data.value)
INSERT INTO YOUR_TABLE (YOUR_FIELD)
SELECT '1' FROM YOUR_TABLE YT WHERE YT.YOUR_FIELD <> '1' LIMIT 1
Of course, that '1' will be your number or your variable.
You can use INSERT + SELECT to solve this problem.