i have a table called scanHistory that holds scanID and HostID as foreign key which are actually primary key to their respective tables. I want to write an insert query in SQLite to avoid inserting same values of scanID to its respective HostID meaning No two same hosts can have same scanID something Like:
ScanID - 100 HostID - 1
ScanID - 100 HostID - 2
ScanID - 200 HostID - 1
I tried this query
INSERT INTO scanHistory (NULL,4000,1) Select 1 WHERE NOT EXISTS (SELECT 1 from scanHistory);
But it's giving me error stating
Error: near "NULL": Syntax Error
what i am doing wrong? The table looks like this
CREATE TABLE scanHistory(
ScanHistoryID INTEGER PRIMARY KEY AUTOINCREMENT,
HostID INTEGER REFERENCES host(HostID),
ScanID INTEGER REFERENCES scan(ScanID));
How about just letting the database enforce it?
CREATE UNIQUE INDEX ix_scan_host ON ScanHistory(ScanID, HostID);
Now you can no longer insert duplicate HostID/ScanID pairs.
An SQLfiddle to test with.
If you just mean that ScanID needs to be unique, you can create the unique index just on that, and the database will enforce that no two rows with the same ScanID can be inserted;
CREATE UNIQUE INDEX ix_scan_host ON ScanHistory(ScanID);
If you want an error back when you try to insert a value, just use INSERT INTO... as usual. If you want to ignore the error, use INSERT OR IGNORE INTO....
EDIT: To do it using SQL only, you can use;
INSERT INTO ScanHistory (ScanID, HostID) SELECT 100, 1
WHERE NOT EXISTS (
SELECT 1 FROM ScanHistory WHERE ScanID=100 AND HostID=1
);
Related
Ie, are the following two SQL statements equivalent in SQLite?
CREATE TABLE posts (
id INTEGER PRIMARY KEY
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY ASC
);
Yes they are.
There is no need to specify ASC and beware that if you were to specify DESC, then NO they are then not equivalent (see 4 below) as id INTEGER PRIMARY KEY DESC is an exclusion to the column being an alias of the rowid column as per :-
The exception mentioned above is that if the declaration of a column
with declared type "INTEGER" includes an "PRIMARY KEY DESC" clause, it
does not become an alias for the rowid and is not classified as an
integer primary key. This quirk is not by design. It is due to a bug
in early versions of SQLite. But fixing the bug could result in
backwards incompatibilities. Hence, the original behavior has been
retained (and documented) because odd behavior in a corner case is far
better than a compatibility break.
ROWIDs and the INTEGER PRIMARY KEY
You can use id INTEGER, PRIMARY KEY(id, DESC), but still the order defaults to ASC when retrieving the column as it is an alias of the rowid (see 5 below )
Perhaps consider the following :-
DROP TABLE IF EXISTS posts1;
CREATE TABLE posts1 (
id INTEGER PRIMARY KEY
);
DROP TABLE IF EXISTS posts2;
CREATE TABLE posts2 (
id INTEGER PRIMARY KEY ASC
);
DROP TABLE IF EXISTS posts3;
CREATE TABLE posts3 (
id INTEGER PRIMARY KEY DESC
);
DROP TABLE IF EXISTS posts4;
CREATE TABLE posts4 (
id INTEGER, PRIMARY KEY (id DESC)
);
INSERT INTO posts1 VALUES(null),(null),(null);
INSERT INTO posts2 VALUES(null),(null),(null);
INSERT INTO posts3 VALUES(null),(null),(null);
INSERT INTO posts4 VALUES(null),(null),(null);
SELECT * FROM sqlite_master WHERE name LIKE '%posts%';
SELECT * FROM posts1;
SELECT * FROM posts2;
SELECT * FROM posts3;
SELECT * FROM posts4;
Results
1
The query SELECT * FROM sqlite_master WHERE name LIKE '%posts%'; results in :-
As you can see posts3 is significantly different as the index sqlite_autoindex_posts3_1 has been created
The others do not have a specific index created as the id column is an alias of the rowid column
The data for rowid tables is stored as a B-Tree structure containing
one entry for each table row, using the rowid value as the key. This
means that retrieving or sorting records by rowid is fast. Searching
for a record with a specific rowid, or for all records with rowids
within a specified range is around twice as fast as a similar search
made by specifying any other PRIMARY KEY or indexed value.
ROWIDs and the INTEGER PRIMARY KEY
2
The query SELECT * FROM posts1; results in :-
3
The query SELECT * FROM posts2;, confirms the initial YES answer as per :-
4
The query SELECT * FROM posts3;, may be a little confusing, but shows that id INTEGER PRIMARY KEY DESC does not result in an alias of the rowid and in the case of no value or null being inserted into the column, the value is null rather than an auto generated value. There is no UNIQUE constraint conflict (as nulls are considered as being different values).
5
The query SELECT * FROM posts4; produces the same result as for 1 and 2 even though id INTEGER, PRIMARY KEY (id DESC) was used. Confirming that even if DESC is applied via the column definition that the sort order is still defaults to ASC (unless the ORDER BY clause is used).
Note that this peculiarity is specific to the rowid column or an alias thereof.
See both https://www.sqlite.org/lang_createtable.html#rowid and https://www.sqlite.org/lang_createindex.html for a more complete answer. Shawn's link is specific to INTEGER PRIMARY KEY which matches the example code, but the more general question is not answered explicitly in either location, but can be deduced by reading both.
Under SQL Data Constraints, the first link says
In most cases, UNIQUE and PRIMARY KEY constraints are implemented by creating a unique index in the database. (The exceptions are INTEGER PRIMARY KEY and PRIMARY KEYs on WITHOUT ROWID tables.)
The CREATE INDEX page explains that originally the sort order was ignored and all indices were generated in ascending order. Only as of version 3.3.0 is the DESC order "understood". But even that description is somewhat vague, however altogether it is apparent that ASC is the default.
I have a table
CREATE TABLE "myTable" (
"id" INTEGER NOT NULL,
"name" VARCHAR,
PRIMARY KEY ("id")
)
and let's say it has 1 record
1 - James
I want to insert a new record. If it doesn't exist, insert it. If it does, do nothing.
I am not sure if the below query, is the right way to do this:
INSERT or IGNORE INTO myTable(id, name) VALUES(1, "Tom");
I tried it and I didn't get any error..
Your statement is fine for what you want to do. However, you don't need to supply a value for id in SQLite. You can just do:
INSERT INTO myTable(name)
VALUES('Tom');
This will auto-increment the id, so you don't have to worry about duplicates.
You used INSERT IGNORE, and the row won't actually be inserted because it results in a duplicate key. Your id column is a PRIMARY KEY, and you already have the value 1 stored in the database.
The statement won't generate an error but it will generate a warning.
I'm trying to rename a column of a table. I have a lot of tables with the word "couleur" and I renamed "manually" to "bulle".
I've successfully renamed main_groupecouleurs to main_groupebulles. Now i'm working on main_groupe. I'm trying to rename groupe_couleurs_id to groupe_bulles_id
The SQL is quite self-explaining:
BEGIN TRANSACTION;
DROP INDEX main_groupe_fc5cee5b;
CREATE TABLE main_groupe7e12
(
id INTEGER PRIMARY KEY NOT NULL,
description TEXT NOT NULL,
exemple TEXT,
groupe_bulles_id INTEGER DEFAULT NULL,
reference TEXT,
FOREIGN KEY (groupe_bulles_id) REFERENCES main_groupebulles(id)
DEFERRABLE INITIALLY DEFERRED
);
CREATE UNIQUE INDEX main_groupe_fc5cee5b ON main_groupe7e12 (groupe_bulles_id);
INSERT INTO main_groupe7e12(id, description, exemple, groupe_bulles_id, reference)
SELECT id, description, exemple, groupe_couleurs_id, reference
FROM main_groupe;
DROP TABLE main_groupe;
ALTER TABLE main_groupe7e12 RENAME TO main_groupe;
COMMIT;
When I run it, I get:
[SQLITE_CONSTRAINT] Abort due to constraint violation
(UNIQUE constraint failed: main_groupe7e12.groupe_bulles_id)
This means (I think I'm wrong here but I dont know what I'm missing) that it tries to insert some groupe_couleurs_id that are not in the referring table (= main_groupebulles). Thus I tried to see in the original table the problem:
SELECT * FROM main_groupe WHERE groupe_couleurs_id NOT IN (
SELECT id FROM main_groupebulles
);
I got no rows! What am I missing?
You have an UNIQUE index on your groupe_bulles_id column but based on the comments, there are a lot of valid duplicate values for that column coming from main_groupe.groupe_couleus_id and that causes the constraint violation.
Since having duplicate values is what you want, remove the UNIQUE from the CREATE UNIQUE INDEX ....
In case you have several tables inside any sqlite database how could the get the information that they have an auto increment primary key or not?
For instance I am already aware that you could get some info concerning the columns of a table by simply querying this: pragma table_info(tablename_in_here)
It would be much better to get the auto increment column dynamically rather than setting up each corresponding model inside the source code with a boolean value.
Edit:
Let me use this table as an example:
CREATE TABLE "test" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" TEXT NOT NULL
)
and this is the result table after executing pragma table_info("test")
cid | name | type | notnull | dflt_value | pk
0 | id | INTEGER | 1 | null | 1
1 | name | TEXT | 1 | null | 0
As you can see there is no information whether the id column is autoincrement or not
Edit2:
I looking for a solution that involves sqlite directly through a statement.
Special situations where the sqlite3 command in the terminal can be used to somehow parse the required information from inside are not acceptable. They do not work in situations where you are not allowed to execute commands in a terminal programmatically. Like in an Android app.
Autoincrementing primary keys must be declared as INTEGER PRIMARY KEY or some equivalent, so you can use the table_info date to detect them.
A column is an INTEGER PRIMARY KEY column if, in the PRAGMA table_info output,
the type is integer or INTEGER or any other case-insensitive variant; and
pk is set; and
pk is not set for any other column.
To check whether the column definition includes the AUTOINCREMENT keyword, you have to look directly into the sqlite_master table; SQLite has no other mechanism to access this information.
If this query returns a record, you have the AUTOINCREMENT keyword somewhere in the table definition (which might return a wrong result if this word is commented out):
SELECT 1
FROM sqlite_master
WHERE type = 'table'
AND name = 'tablename_in_here'
AND sql LIKE '%AUTOINCREMENT%'
You can parse the output of .schema. That will give you the sql commands as you used them to create your tables. If autoincrement was declared, you will see it in the output. This has the advantage that it will list all your tables too.
I have a script below - I dont know if it will produce the same effect as auto increment. When i begin inserting rows into my database, i dont want to insert the id. I want the database to generate and insert them automatically when i insert non-id rows.
CREATE TABLE myschema.mytable
(id NUMBER PRIMARY KEY NOT NULL,
name VARCHAR2(30));
CREATE SEQUENCE myschema.test1_sequence
START WITH 1
INCREMENT BY 1;
create or replace trigger myschema.auto_increment
before insert on myschema.mytable
for each row
begin
select test1_sequence.nextval into :new.id from dual;
end;
/
Yes it will work, except that you don't have to use
id NUMBER PRIMARY KEY NOT NULL
because PRIMARY KEY already contains the NOT NULL constraint, so
id NUMBER PRIMARY KEY
is enough.