"Insert if not exists" statement in SQLite - sqlite

I have an SQLite database. I am trying to insert values (users_id, lessoninfo_id) in table bookmarks, only if both do not exist before in a row.
INSERT INTO bookmarks(users_id,lessoninfo_id)
VALUES(
(SELECT _id FROM Users WHERE User='"+$('#user_lesson').html()+"'),
(SELECT _id FROM lessoninfo
WHERE Lesson="+lesson_no+" AND cast(starttime AS int)="+Math.floor(result_set.rows.item(markerCount-1).starttime)+")
WHERE NOT EXISTS (
SELECT users_id,lessoninfo_id from bookmarks
WHERE users_id=(SELECT _id FROM Users
WHERE User='"+$('#user_lesson').html()+"') AND lessoninfo_id=(
SELECT _id FROM lessoninfo
WHERE Lesson="+lesson_no+")))
This gives an error saying:
db error near where syntax.

If you never want to have duplicates, you should declare this as a table constraint:
CREATE TABLE bookmarks(
users_id INTEGER,
lessoninfo_id INTEGER,
UNIQUE(users_id, lessoninfo_id)
);
(A primary key over both columns would have the same effect.)
It is then possible to tell the database that you want to silently ignore records that would violate such a constraint:
INSERT OR IGNORE INTO bookmarks(users_id, lessoninfo_id) VALUES(123, 456)

If you have a table called memos that has two columns id and text you should be able to do like this:
INSERT INTO memos(id,text)
SELECT 5, 'text to insert'
WHERE NOT EXISTS(SELECT 1 FROM memos WHERE id = 5 AND text = 'text to insert');
If a record already contains a row where text is equal to 'text to insert' and id is equal to 5, then the insert operation will be ignored.
I don't know if this will work for your particular query, but perhaps it give you a hint on how to proceed.
I would advice that you instead design your table so that no duplicates are allowed as explained in #CLs answer below.

For a unique column, use this:
INSERT OR REPLACE INTO tableName (...) values(...);
For more information, see: sqlite.org/lang_insert

insert into bookmarks (users_id, lessoninfo_id)
select 1, 167
EXCEPT
select user_id, lessoninfo_id
from bookmarks
where user_id=1
and lessoninfo_id=167;
This is the fastest way.
For some other SQL engines, you can use a Dummy table containing 1 record.
e.g:
select 1, 167 from ONE_RECORD_DUMMY_TABLE

Related

Inserting rows from table2 to end of table1 with unique ids

I'm trying to add a test user to my website that employers can look at to see my work. I want to use some of the data I have entered into my profile so that it is faster.
I have a workouts table:
CREATE TABLE workouts(
id INTEGER NOT NULL,
userID INTEGER NOT NULL,
DateAndTime smalldatetime NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (UserID) REFERENCES users(id)
);
I have taken 25 of the first results and put it into a temporary workouts2 table:
CREATE TABLE workouts2 (
userid integer,
dateandtime smalldatetime);
Now I want to take those rows from workouts2 and put them into workouts. I have tried to add them by inserting workouts2 into workouts like this:
insert into workouts (id , userID, DateandTime) values (select * from workouts2);
This gives me an Error: in prepare, near "select": syntax error (1)
I can do it one at a time like this:
insert into workouts (userid, dateandtime) values (2, "2022-01-02T06:00");
Doing it one at a time is not ideal.
What am I missing here? I know I have a syntax error but I don't know how to fix it.
I have looked at this question which inserts one at a time:
How to insert a unique ID into each SQLite row?
The problem is it only inserts one at a time.
You should use SELECT instead of VALUES and not include the column id, which is auto-incremented, in the list of columns of workouts which will receive the values (such a column does not exist in workouts2):
INSERT INTO workouts (userID, DateandTime)
SELECT *
FROM workouts2;

How to write a delete trigger in Oracle PL/SQL - stuck on identifying "matching rows"

I have a trigger I'm writing whereby , once I delete a row, I want to delete the corresponding row in the another table (which is common_cis.security_function ).
and the source table is party.security_function
Here are the columns in common_cis.security_function :
URL
SCRTY_FUNC_NAME
SCRTY_FUNC_DESC
IDN
CREATE_TMSTMP
CNCRCY_USER_IDN
Here are the columns in party.security_function :
UPDATE_USER_SRC_SYS_CD
UPDATE_USER_ID
UPDATE_TS
SCRT_FUNC_NM
SCRT_FUNC_DESC
CREAT_USER_SRC_SYS_CD
CREAT_USER_ID
CREAT_TS
What I have so far is :
delete from common_cis.security_function CCSF
where CCSF.SCRTY_FUNC_NAME = :new.SCRT_FUNC_NM;
Is this the right idea? Or do I use some kind of row-ID ?
thanks
I think you should use integrity constraints for that, namely foreign key constraint with "ON DELETE CASCADE" condition.
Here is an example, but check first is there tables in your schema with names that I used:
-- create tables:
create table master_table(
URL varchar2(1000),
SCRTY_FUNC_NAME varchar2(100),
SCRTY_FUNC_DESC varchar2(1000));
create table detail_table(
SCRT_FUNC_NM varchar2(100),
SCRT_FUNC_DESC varchar2(1000),
UPDATE_USER_ID number,
UPDATE_TS varchar2(100));
-- add primary key and foreign key constraints:
alter table master_table add constraint function_pk primary key (SCRTY_FUNC_NAME);
alter table detail_table add constraint function_fk foreign key (SCRT_FUNC_NM) references master_table (SCRTY_FUNC_NAME) on delete cascade;
-- fill tables with data:
insert into master_table
values ('url number 1', 'sec function #1', 'description of function #1');
insert into detail_table
values('sec function #1', 'description', 1, '123abc');
insert into detail_table
values('sec function #1', 'description', 2, '456xyz');
-- check tables: first contains 1 row and second - 2 rows
select count(*) from master_table;
select count(*) from detail_table;
-- delete rows from first table only:
delete from master_table;
-- check tables once again - both are empty:
select count(*) from master_table;
select count(*) from detail_table;
-- clear test tables:
drop table detail_table;
drop table master_table;

bulk insert using insert...select in sqlite?

I have a list of users who should receive the message. They are in the table subscribe. Now I'd like to insert a message for every one of these users. My query is
insert into message(user, type, theId)
select (select user from subscribe_message), #type, #id
At the moment it is empty. I get the error message.user may not be NULL. Shouldn't it not insert any rows? When I have more than one row it inserts the first row only. How do I write this so it will insert 0 to many rows?
Try this
INSERT INTO message ( user, type, theId )
SELECT subscribe_message.user, #type, #id
FROM subscribe_message

sqlite3 if exists alternative

I am trying to run simple query for sqlite which is update a record if not exists.
I could have used Insert or replace but article_tags table doesn't have any primary key as it is a relational table.
How can I write this query for sqlite as if not exists is not supported.
And I don't have idea how to use CASE for this ?
Table Structure:
articles(id, content)
article_tags(article_id, tag_id)
tag(id, name)
SQLITE Incorrect Syntax Tried
insert into article_tags (article_id, tag_id ) values ( 2,7)
if not exists (select 1 from article_tags where article_id =2 AND tag_id=7)
I think the correct way to do this would be to add a primary key to the article_tags table, a composite one crossing both columns. That's the normal way to do many-to-many relationship tables.
In other words (pseudo-DDL):
create table article_tags (
article_id int references articles(id),
tag_id int references tag(id),
primary key (article_id, tag_id)
);
That way, insertion of a duplicate pair would fail rather than having to resort to if not exists.
This seems to work, although I will, as others have also said, suggest that you add some constraints on the table and then simply "try" to insert.
insert into article_tags
select 2, 7
where not exists ( select *
from article_tags
where article_id = 2
and tag_id = 7 )

INSERT IF NOT EXISTS ELSE UPDATE?

I've found a few "would be" solutions for the classic "How do I insert a new record or update one if it already exists" but I cannot get any of them to work in SQLite.
I have a table defined as follows:
CREATE TABLE Book
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
What I want to do is add a record with a unique Name. If the Name already exists, I want to modify the fields.
Can somebody tell me how to do this please?
Have a look at http://sqlite.org/lang_conflict.html.
You want something like:
insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);
Note that any field not in the insert list will be set to NULL if the row already exists in the table. This is why there's a subselect for the ID column: In the replacement case the statement would set it to NULL and then a fresh ID would be allocated.
This approach can also be used if you want to leave particular field values alone if the row in the replacement case but set the field to NULL in the insert case.
For example, assuming you want to leave Seen alone:
insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
(select ID from Book where Name = "SearchName"),
"SearchName",
5,
6,
(select Seen from Book where Name = "SearchName"));
You should use the INSERT OR IGNORE command followed by an UPDATE command:
In the following example name is a primary key:
INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'
The first command will insert the record. If the record exists, it will ignore the error caused by the conflict with an existing primary key.
The second command will update the record (which now definitely exists)
You need to set a constraint on the table to trigger a "conflict" which you then resolve by doing a replace:
CREATE TABLE data (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);
Then you can issue:
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);
The "SELECT * FROM data" will give you:
2|2|2|3.0
3|1|2|5.0
Note that the data.id is "3" and not "1" because REPLACE does a DELETE and INSERT, not an UPDATE. This also means that you must ensure that you define all necessary columns or you will get unexpected NULL values.
INSERT OR REPLACE will replace the other fields to default value.
sqlite> CREATE TABLE Book (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
);
sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);
sqlite> SELECT * FROM Book;
1001|C++|10|10|0
sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');
sqlite> SELECT * FROM Book;
1001|SQLite|||
If you want to preserve the other field
Method 1
sqlite> SELECT * FROM Book;
1001|C++|10|10|0
sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001);
sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;
sqlite> SELECT * FROM Book;
1001|SQLite|10|10|0
Method 2
Using UPSERT (syntax was added to SQLite with version 3.24.0 (2018-06-04))
INSERT INTO Book (ID, Name)
VALUES (1001, 'SQLite')
ON CONFLICT (ID) DO
UPDATE SET Name=excluded.Name;
The excluded. prefix equal to the value in VALUES ('SQLite').
Firstly update it. If affected row count = 0 then insert it. Its the easiest and suitable for all RDBMS.
Upsert is what you want. UPSERT syntax was added to SQLite with version 3.24.0 (2018-06-04).
CREATE TABLE phonebook2(
name TEXT PRIMARY KEY,
phonenumber TEXT,
validDate DATE
);
INSERT INTO phonebook2(name,phonenumber,validDate)
VALUES('Alice','704-555-1212','2018-05-08')
ON CONFLICT(name) DO UPDATE SET
phonenumber=excluded.phonenumber,
validDate=excluded.validDate
WHERE excluded.validDate>phonebook2.validDate;
Be warned that at this point the actual word "UPSERT" is not part of the upsert syntax.
The correct syntax is
INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...
and if you are doing INSERT INTO SELECT ... your select needs at least WHERE true to solve parser ambiguity about the token ON with the join syntax.
Be warned that INSERT OR REPLACE... will delete the record before inserting a new one if it has to replace, which could be bad if you have foreign key cascades or other delete triggers.
If you have no primary key, You can insert if not exist, then do an update. The table must contain at least one entry before using this.
INSERT INTO Test
(id, name)
SELECT
101 as id,
'Bob' as name
FROM Test
WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;
Update Test SET id='101' WHERE name='Bob';
I believe you want UPSERT.
"INSERT OR REPLACE" without the additional trickery in that answer will reset any fields you don't specify to NULL or other default value. (This behavior of INSERT OR REPLACE is unlike UPDATE; it's exactly like INSERT, because it actually is INSERT; however if what you wanted is UPDATE-if-exists you probably want the UPDATE semantics and will be unpleasantly surprised by the actual result.)
The trickery from the suggested UPSERT implementation is basically to use INSERT OR REPLACE, but specify all fields, using embedded SELECT clauses to retrieve the current value for fields you don't want to change.
I think it's worth pointing out that there can be some unexpected behaviour here if you don't thoroughly understand how PRIMARY KEY and UNIQUE interact.
As an example, if you want to insert a record only if the NAME field isn't currently taken, and if it is, you want a constraint exception to fire to tell you, then INSERT OR REPLACE will not throw and exception and instead will resolve the UNIQUE constraint itself by replacing the conflicting record (the existing record with the same NAME). Gaspard's demonstrates this really well in his answer above.
If you want a constraint exception to fire, you have to use an INSERT statement, and rely on a separate UPDATE command to update the record once you know the name isn't taken.

Resources