Sqlite delete slow in 4GB - sqlite

I have been using fat32 file system. I have a database which has 4 GB size. Database consist of 1 parent table and 1 child table. Parent table has 10 rows and child table has 4000 rows. 1 row of child table has 1 MB size.
When I delete a row in parent table, deletion cascades 1MB-sized child records. (pragma foreign_keys is on) When I try to delete 100 MB data by cascade (1 parent record - 100 child records) it takes too long time (almost 1-10 minute) to complete, and the duration increase/decrease by size of data (100 Mb: 1-10 minute, 300 MB: 3-30 minute,etc).
I tried some pragma commands (synchronous, temp_store, journal_mode) suggested by others posts and i also tried to add index on foreign key, but those does not help solve my problem.(Actually, after adding index on foreign key, 1 MB data deletion became faster/st, but 100 MB data deletion duration did not change) Can you give me please any suggestion to increase deletion performance?
CREATE TABLE "ANHXT" (
"id" integer primary key autoincrement,
"ANH_AD" text,
"ANH_DBGMHWID" text,
"ANH_TYPE" integer,
"ANH_INDEXNO" int64_t
)
CREATE TABLE "PRCXT" (
"id" integer primary key autoincrement,
"ANP_SEGMENTNO" integer not null,
"ANP_VALUE" blob,
"ANH_PRC_id" bigint,
constraint "fk_ANHPRC_ANH_PRC" foreign key ("ANH_PRC_id") references "ANHXT" ("id") on update cascade on delete cascade deferrable initially deferred
)
CREATE UNIQUE INDEX UQC_ANH_TYPE on ANHXT( ANH_TYPE)
CREATE UNIQUE INDEX UQC_ANP_SEGMENTNO_ANAHTARID on PRCXT( ANP_SEGMENTNO,ANH_PRC_id)
CREATE INDEX findex on PRCXT( ANH_PRC_id)

Related

Does PRIMARY KEY constraint defined on table level guarantees AUTOINCREMENT and no values reuse?

Consider the following table definition:
CREATE TABLE names (
id INTEGER,
name TEXT NOT NULL,
PRIMARY KEY (id)
)
Does it guarantee that the id will be auto-incremented for every new insert AND that the values for deleted rows will not be reused?
I looked up in the documentation for Sqlite3, but couldn't find the answer.
id INTEGER PRIMARY KEY on it's own guarantees (requires) a unique integer value and will if no value is specifically assigned provide one until the highest value has reached the highest allowed value for a 64 bit signed integer (9223372036854775807) after which an unused value may be found and applied.
With AUTOINCREMENT there is a guarantee (if not circumvented) of always providing a higher value BUT if 9223372036854775807 is reached instead of allocating an unused number an SQLITE_FULL error will result. That is the only difference from the point of view of what number will be assigned.
Neither guarantees a monotonically increasing value.
Without AUTOINCREMENT the calculation/algorithm is equivalent to
1 + max(rowid) and if the value is greater than 9223372036854775807 an attempt is made to find an unused and therefore lower value.
I've not seen that anyone has come across the situation where a random unused value has not been assigned.
With AUTOINCREMENT the calculation/algorithim is
the greater of 1 + max(rowid) or SELECT seq FROM sqlite_sequence WHERE name = 'the_table_name_the_rowid_is_being_assigned_to' and if the value is greater than 9223372036854775807 then SQLITE_FULL ERROR.
noting that either way there is the possibility that the max rowid is for a row that eventually doesn't get inserted and therefore the potential for gaps.
The answer is perhaps best put as: it's best/recommended to use the id column solely for it's intended purpose, that of efficiently identifying a row and not as a means of handling other data requirements, and if done so, there there is no need for AUTOINCREMENT (which has overheads)
In short
Does it guarantee that the id will be auto-incremented
NO
values for deleted rows will not be reused?
NO for the given code
for :-
CREATE TABLE names (id INTEGER PRIMARY KEY AUTOINCREMENT name TEXT NOT NULL)
again NO as if 9223372036854775807 is reached then an SQLITE_FULL error will result, otherwise YES.
So really AUTOINCREMENT is only really relevant (if the id used as expected/intended) when the 9223372036854775807'th row has been inserted.
Perhaps consider the following :-
DROP TABLE IF EXISTS table1;
DROP TABLE IF EXISTS table2;
CREATE TABLE IF NOT EXISTS table1 (id INTEGER PRIMARY KEY, somecolumn TEXT);
CREATE TABLE IF NOT EXISTS table2 (id INTEGER PRIMARY KEY AUTOINCREMENT, somecolumn TEXT);
INSERT INTO table1 VALUES (9223372036854775807,'blah');
INSERT INTO table2 VALUES (9223372036854775807,'blah');
INSERT INTO table1 (somecolumn) VALUES(1),(2),(3);
SELECT * FROM table1;
INSERT INTO table2 (somecolumn) VALUES(1),(2),(3);
This creates the two similar tables, the only difference being the use of AUTOINCREMENT. Each has a row inserted with the highest allowable value for the id column.
An attempt is then made to insert 3 rows where the id will be assigned by SQLite.
3 rows are inserted into the table without AUTOINCREMENT but no rows are inserted when AUTOINCREMENT is used. as per :-
CREATE TABLE IF NOT EXISTS table1 (id INTEGER PRIMARY KEY, somecolumn TEXT)
> OK
> Time: 0.098s
CREATE TABLE IF NOT EXISTS table2 (id INTEGER PRIMARY KEY AUTOINCREMENT, somecolumn TEXT)
> OK
> Time: 0.098s
INSERT INTO table1 VALUES (9223372036854775807,'blah')
> Affected rows: 1
> Time: 0.094s
INSERT INTO table2 VALUES (9223372036854775807,'blah')
> Affected rows: 1
> Time: 0.09s
INSERT INTO table1 (somecolumn) VALUES(1),(2),(3)
> Affected rows: 3
> Time: 0.087s
SELECT * FROM table1
> OK
> Time: 0s
INSERT INTO table2 (somecolumn) VALUES(1),(2),(3)
> database or disk is full
> Time: 0s
The result of the SELECT for table1 (which may differ due to randomness) was :-

Expo sqlite foreign key

Did anyone work on react native expo's sqlite database with foreign key constraints? Can we use structure similar to sql?
I'm trying to work on it building multiple tables with foreign key condition.
Example: If we have 2 tables Persons and Orders where personID is referred as foreign key in orders table. How would it be done using sqlite?
You would have two tables, perhaps with a column as the alias of the rowid column, this
e.g.
CREATE TABLE persons (
personid INTEGER PRIMARY KEY,
personname TEXT
);
CREATE TABLE orders (
orderid INTEGER PRIMARY KEY,
ordername TEXT,
person_reference INTEGER REFERENCES persons(personid)
);
Note that you have to turn foreign key handling on e.g. by executing PRAGMA foreign_keys = ON; (or true). See PRAGMA foreign_keys
in SQLite coding column_name INTEGER PRIMARY KEY defines that column as an alias of the rowid column, and if a value is not provided for the column when inserting then an integer value will be assigned. The initial value for the first row will be 1, subsequent values will typically be 1 greater than the highest rowid value (read the link above in regards why the word typically has been used).
If you then try to insert an Order for a non-existent personid you will then get a Foreign Key conflict.
An alternative to the column level definition would be to define the foreign key(s) at the table level e.g.
CREATE TABLE orders (
orderid INTEGER PRIMARY KEY,
ordername TEXT,
person_reference INTEGER,
FOREIGN KEY (person_reference) REFERENCES persons(personid)
);
As an example, consider the following :-
INSERT INTO persons (personname) VALUES
('Fred'),
('Mary'),
('Sue'),
('Tom')
;
INSERT INTO orders (ordername, person_reference) VALUES
('Order 1 for Fred',1),
('Order 2 for Sue',3),
('Order 3 for Fred',1),
('Order 4 for Mary',2)
;
INSERT into orders (ordername, person_reference) VALUES
('Order 5 for nobody',100);
The result would be :-
INSERT INTO persons (personname) VALUES ('Fred'),('Mary'),('Sue'),('Tom')
> Affected rows: 4
> Time: 0.453s
INSERT INTO orders (ordername, person_reference) VALUES
('Order 1 for Fred',1),('Order 2 for Sue',3),('Order 3 for Fred',1),('Order 4 for Mary',2)
> Affected rows: 4
> Time: 0.084s
INSERT into orders (ordername, person_reference) VALUES
('Order 5 for nobody',100)
> FOREIGN KEY constraint failed
> Time: 0s
i.e. the last as there is no row in the persons table with a personid of 100, then the last insert (on it's own doe demonstration) fails.
You may wish to refer to SQLite Foreign Key Support

SQLite AUTOINCREMENT non-primary key column

I have the following SQLite table:
CREATE TABLE podcast_search (
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
search TEXT NOT NULL UNIQUE
)
Whenever a user inserts/updates a row in the table, I want to sort that row at the end of the table. Thus, if I insert the following values:
_id | search | sort
===================
1 | foo | 1
2 | bar | 2
3 | quiz | 3
And then later update the 1 row from foo to foo2, the values should look like:
_id | search | sort
===================
2 | bar | 2
3 | quiz | 3
1 | foo2 | 4
I've implemented this thusly:
CREATE TABLE podcast_search (
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
search TEXT NOT NULL UNIQUE,
update_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
CREATE TRIGGER update_date_update_trigger
AFTER UPDATE ON podcast_search FOR EACH ROW
BEGIN
UPDATE podcast_search
SET update_date = CURRENT_TIMESTAMP
WHERE _id = OLD._id;
END
However, my unit tests require a 1000ms sleep between insert/update operations in order to reliably sort, and this amount of delay is very annoying for unit testing.
I thought I could implement a vector clock instead, but it seems that AUTOINCREMENT values only exist for primary key columns. Does SQLite offer any other AUTOINCREMENT or AUTOINCREMENT-like option?
I'm running this on Android P, but this should be a generic SQLite problem.
UPDATE
I'm now using an sort INTEGER NOT NULL UNIQUE column, and SELECT-ing the largest row in that column and manually incrementing it before an INSERT/UPDATE:
CREATE TABLE podcast_search (
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
search TEXT NOT NULL UNIQUE,
sort INTEGER NOT NULL UNIQUE
)
SELECT sort from podcast_search ORDER BY sort DESC
either increment sort in application code, or set it to 0
Could I do this in a TRIGGER instead?
I thought I could implement a vector clock instead, but it seems that
AUTOINCREMENT values only exist for primary key columns. Does SQLite
offer any other AUTOINCREMENT or AUTOINCREMENT-like option?
They are not in fact AUTOINCREMENT values rather a column with AUTOINCREMENT will be an alias of the rowid column; not because AUTOINCREMENT has been coded but because INTEGER PRIMARY KEY has been coded.
All coding AUTOINCREMENT does is add a constraint that an auto-generated value MUST be greater than any other existing or used value. This only in fact becomes apparent if when a rowid with the value of 9223372036854775807 exists. In which case an attempt to insert a new row with an auto-generated rowid (i.e. no value is specified for the rowid column or an alias thereof) will result in an SQLITE_FULL error.
Without AUTOINCREMENT and when the highest rowid is 9223372036854775807 (the highest possible value for a rowid) an attempt is made to use a free value, which would obviously be lower than 9223372036854775807.
SQLite Autoincrement
You may wish to note the very first line of the linked page which says :-
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and
disk I/O overhead and should be avoided if not strictly needed. It is
usually not needed.
I can't see any need from your description.
So what you want is a means of assigning a value for the column that is to be sorted that is 1 greater than the highest current value for that column, so it becomes the latest for sorting purposes, a subquery that retrieves max(the_column) + 1 would do what you wish. This could be in an UPDATE, TRIGGER or in an INSERT.
rowid = max(rowid) + 1 is basically how SQLite assigns a value to rowid unless AUTOINCREMENT is used when 1 is added to the greater of max(rowid) and the value, for the respective table, obtained from the table sqlite_sequence (will only exist if AUTOINCREMENT is used). It is referencing and maintaining sqlite_sequence that incurs the penalties.
For example you could use the following (which eliminates the need for an additional column and the additional index) :-
-- SETUP THE DATA FOR TESTING
DROP TABLE IF EXISTS podcast_searchv1;
CREATE TABLE IF NOT EXISTS podcast_searchv1 (
_id INTEGER NOT NULL PRIMARY KEY,
search TEXT NOT NULL UNIQUE
);
INSERT INTO podcast_searchv1 (search)
VALUES('foo'),('bar'),('guide')
;
-- Show original data
SELECT * FROM podcast_searchv1;
-- DO THE UPDATE
UPDATE podcast_searchv1 SET search = 'new value', _id = (SELECT max(_id) + 1 FROM podcast_searchv1) WHERE search = 'foo';
-- Show the changed data
SELECT * FROM podcast_searchv1;
The results being :-
and then :-

Deleting a record in parent table and updating "DELETED" column in child table

I have a parent table(profiles) in which profile_id is the primary key and is a foreign key to 3 different child tables. (s_profile, p_profile, c_profile)
Now, i want to delete a record from the table profile and want to update a "DELETED" column in the child tables with sysdate.
However my script doesn't allow it saying "Foreign key violated- Child record found".
Is there a solution to it?
What is the purpose of that foreign key constraint, if you want to allow deleting master while details exist?
Anyway, here's an example which shows what you might do; is it the right way, I can't tell (I suspect not - once again, it is cancels the purpose of the referential constraint).
Create two tables - master and its detail:
SQL> create table profiles
2 (id_profile number primary key);
Table created.
SQL> create table s_profile
2 (id number primary key,
3 id_profile number constraint fk_s_pro references profiles (id_profile),
4 deleted date);
Table created.
SQL>
Sample data and attempt to delete a master while detail exists:
SQL> insert into profiles values (1);
1 row created.
SQL> insert into s_profile (id, id_profile) values (100, 1);
1 row created.
SQL> delete from profiles where id_profile = 1;
delete from profiles where id_profile = 1
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_S_PRO) violated - child record found
SQL>
Create a trigger on the master table which removes foreign key value and sets the date:
SQL> create or replace trigger trg_bd_prof
2 before delete on profiles
3 for each row
4 begin
5 update s_profile s set
6 s.id_profile = null,
7 s.deleted = sysdate
8 where s.id_profile = :old.id_profile;
9 end;
10 /
Trigger created.
Let's try to delete master again:
SQL> delete from profiles where id_profile = 1;
1 row deleted.
SQL> select * From s_profile;
ID ID_PROFILE DELETED
---------- ---------- ----------
100 2018-10-03
SQL>
If you want to save foreign key value, you could alter detail table and add another column, say deleted_id_profile and populate it with the same trigger. Though, what would you do with it, if parent doesn't exist any more and you can't find any info about it?

SQL: How to select duplicate values

My SQLite table tPeople has a primary key on field fLocation of type UNSIGNED INTEGER (which is not the rowid).
It also has a secondary index on field fLastName of type TEXT which is filled with last names - and it naturally has lots of duplicate keys.
Let's say my display can handle 500 rows at a time so I...
SELECT * FROM tpeople WHERE fLastName = 'Smith' LIMIT 500
...but there could be 2,600 fLastNames equal to 'Smith' or maybe just 3 of them.
How do I select the next 500 rows with fLastName = 'Smith'?

Resources