How to use SQLite database created with custom collation, in an environment where this collation is unavailable? - sqlite

Suppose I register a custom collation my_collation which, for example, orders digit strings as integers. Then I create and populate a table foo with a primary key on the column bar using this collation:
CREATE TABLE foo(
bar TEXT PRIMARY KEY COLLATE my_collation,
quux INT
);
INSERT INTO foo VALUES ('1000', 1000), ('1', 1), ('123', 123), ('12', 12);
When I subsequently open this database in an environment that does not have this custom collation registered (e.g. sqlite3 CLI shell) I'm unable to query this table:
sqlite> SELECT * FROM foo;
Parse error: no query solution
Obviously I don't expect to be able to INSERT/DELETE, UPDATE the bar column or even to SELECT with constraints on bar, e.g.
SELECT * FROM foo
WHERE bar = '12';
since all these require comparing bar values via the unavailable collation. But I don't understand why I can't just SELECT the entire table.
Why can't I, and how do I get the data out?

Related

How to explicitly change query plan for sqlite3 [duplicate]

I am doing performance analysis over mondial database using sqlite3. One test case where I have to compare performance with and without using index (it should not use sqlite_autoindex as well).
I found this link :How can I force a query to not use a index on a given table? very useful but most of the answers refer to SQL SERVER. But I need it for SQLITE3. (I have tried PRAGMA options but no result).
It's buried in the syntax diagrams for SELECT, but there is a way - Using NOT INDEXED with a table name in the FROM clause:
sqlite> CREATE TABLE foo(bar);
sqlite> CREATE INDEX foo_idx ON foo(bar);
sqlite> EXPLAIN QUERY PLAN SELECT * FROM foo WHERE bar = ?;
QUERY PLAN
`--SEARCH TABLE foo USING COVERING INDEX foo_idx (bar=?)
sqlite> EXPLAIN QUERY PLAN SELECT * FROM foo NOT INDEXED WHERE bar = ?;
QUERY PLAN
`--SCAN TABLE foo
As you can see, the first query uses the index, and the second one doesn't.
How to read EXPLAIN QUERY PLAN output.

SQLite FTS5 Match is returning nothing

I have a SQLite3 table:
CREATE TABLE "test" (
"title" TEXT,
"shortdesc" TEXT,
"longdesc" TEXT,
"id" TEXT,
PRIMARY KEY("id")
);
I insert anything in it:
INSERT INTO test (id, title, shortdesc, longdesc) VALUES ("abc", "hello world", "this is a hello world", "a nice hello world article about hello worlds")
Then I create a FTS5 virtual table:
CREATE VIRTUAL TABLE IF NOT EXISTS test_fts USING fts5 (
id,
title,
shortdesc,
longdesc,
content=test
);
So I check data in virtual table:
Everything seems fine... Now I try to use MATCH to find the article:
SELECT * FROM test_fts WHERE test_fts MATCH 'hello'
...and I get nothing in result. Obviously this database I showed is just an example, same thing happens with actual database. I tried on different computers (and different clients), I also checked if FTS5 is enabled and compiled in it with PRAGMA compile_options and ENABLE_FTS5 is there, meaning it's enabled. Same thing happens with FTS3 and 4.
So what am I missing? SQLite version is 3.36.0.
The FTS5 tables still need to be populated. This includes external content cases. From the documentation:
It is still the responsibility of the user to ensure that the contents of an external content FTS5 table are kept up to date with the content table. One way to do this is with triggers. For example:
-- Create a table. And an external content fts5 table to index it.
CREATE TABLE tbl(a INTEGER PRIMARY KEY, b, c);
CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a');
-- Triggers to keep the FTS index up to date.
CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGIN
INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;
CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGIN
INSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES('delete', old.a, old.b, old.c);
END;
CREATE TRIGGER tbl_au AFTER UPDATE ON tbl BEGIN
INSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES('delete', old.a, old.b, old.c);
INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;
So, you could either create the table and triggers before populating the main table, or in your test case, after you create the FTS table you can run a query like this to populate the FTS table for the first time:
INSERT INTO test_fts SELECT * FROM test;
Then your query will work as expected.

SQlite allows auto increment creation of a primary key referencing another table

I have those two tables implementing some inheritance relationship via the Class Table Inheritance pattern:
pragma foreign_keys = ON;
create table foo(foo_id integer primary key);
create table bar(foo_id integer primary key references foo(foo_id));
Let's populate foo:
insert into foo values (1), (3), (4);
Now I can insert 3 into bar:
insert into bar values(3); -- no error
I cannot insert 2:
insert into bar values(2); -- Error: FOREIGN KEY constraint failed
However, what suprises me is that NULL values can be used to generate new keys:
insert into bar values(NULL); -- OK, 4 inserted
insert into bar values(NULL); -- FOREIGN KEY constraint failed
This behavior seems rather odd. When I try the same thing in MySQL, I am greeted with a
ERROR 1048 (23000): Column 'foo_id' cannot be null
which is what I would expect.
I find this behavior particularly dangerous when inserting new rows in bar with a subquery:
insert into bar (foo_id) values ((select foo_if from foo where ...))
which could end up silently inserting random rows into bar when there is no match in foo, instead of returning an error.
Is this behavior compliant with the SQL standard, in what scenario could it be useful, and more importantly, is there a way this behavior could be changed to match MySQL's?
EDIT
Here is an illustration of the problem in a perhaps more striking (and scary) fashion:
pragma foreign_keys = ON;
create table people(people_id integer primary key, name text not null);
insert into people (name) values ("Mom"), ("Jack the Ripper");
create table family_member(people_id integer primary key references people(people_id));
insert into family_member values ((select people_id from people where name = "Mom"));
insert into family_member values ((select people_id from people where name = "Dad")); -- silent error here
select name from family_member inner join people using (people_id);
-- uh-oh, Jack the Ripper is now part of my family
So I found an anwer to this problem, which may be a bit surprising for people not familiar with SQLite.
It turns out that a column that is declared integer primary key is automatically filled with an unused integer if it is not given a value.
A somewhat arcane way around this in SQLite is to use declare the column int primary key instead, which prevents the implicit automatic handling.
However, we are not over yet, because another distinctive behavior of SQLite is to allow NULL primary keys -- in our example above, insert into bar values (NULL) would insert a new row with a NULL primary key.
To explicitely ban primary keys from having NULL values, it must be declared not null:
pragma foreign_keys = ON;
create table foo(foo_id integer primary key);
create table bar(foo_id int primary key not null references foo(foo_id));
insert into foo values (1), (3), (4);
insert into bar values (3);
insert into bar values (2); -- Error: FOREIGN KEY constraint failed
insert into bar values (NULL); -- Error: NOT NULL constraint failed: bar.foo_id
This is probably the way primary key pointing to external keys should be declared by default in SQLite.

Problems with SELECT clause in plsql

I m working on a project using oracle client and plsql for testing optimizing tuning etc. Here is the thing,this is a university course project so i m asked to create some tables then run some queries(and then optimize etc),and we have 2 options doing it.Either from linux terminal either from plsql.I create the tables from terminal in the database using sqlplus,then run the queries in plsql,but i have no results(it's like the rows are empty,but they are not). I did some search but i cant find a solution. If you need to know more details tell me.
Thanks in advance.
Are you saying that you create your table in a sqlplus session, insert data into it, and then run a separate session where you're querying the tables using plsql?
If so, are you committing your work after performing the insert?
For example, if you're doing this in sqlplus:
create table foo (id number, value varchar(255), primary key (id));
insert into foo (id, value) values (1, 'bar');
insert into foo (id, value) values (2, 'baz');
commit;
Then you should be to see the 2 rows in the table foo in another session. However, if you're not doing the commit then the new table will be visible to another session but it will appear to be empty.

Can I alter a column in an sqlite table to AUTOINCREMENT after creation?

Can I make a field AUTOINCREMENT after made a table? For example, if you create a table like this:
create table person(id integer primary key, name text);
Then later on realise it needs to auto increment. How do I fix it, ie in MySQL you can do:
alter table person modify column id integer auto_increment
Is table creation the only opportunity to make a column AUTOINCREMENT?
You can dump the content to a new table:
CREATE TABLE failed_banks_id (id integer primary key autoincrement, name text, city text, state text, zip integer, acquired_by text, close_date date, updated_date date);
INSERT INTO failed_banks_id(name, city, state, zip, acquired_by,close_date, updated_date)
SELECT name, city, state, zip, acquired_by,close_date, updated_date
FROM failed_banks;
And rename the table:
DROP TABLE failed_banks;
ALTER TABLE failed_banks_id RENAME TO failed_banks;
Background:
The new key will be unique over all
keys currently in the table, but it
might overlap with keys that have been
previously deleted from the table. To
create keys that are unique over the
lifetime of the table, add the
AUTOINCREMENT keyword to the INTEGER
PRIMARY KEY declaration.
http://www.sqlite.org/faq.html#q1
SQLite limitations:
SQLite supports a limited subset of
ALTER TABLE. The ALTER TABLE command
in SQLite allows the user to rename a
table or to add a new column to an
existing table. It is not possible to
rename a column, remove a column, or
add or remove constraints from a
table.
http://www.sqlite.org/lang_altertable.html
Hack seems to exist:
It appears that you can set
PRAGMA writable_schema=ON;
Then do a manual UPDATE of the
sqlite_master table to insert an "id
INTEGER PRIMARY KEY" into the SQL for
the table definition. I tried it and
it seems to work. But it is
dangerous. If you mess up, you
corrupt the database file.
http://www.mail-archive.com/sqlite-users#sqlite.org/msg26987.html
From the SQLite Faq
Short answer: A column declared INTEGER PRIMARY KEY will autoincrement
So when you create the table, declare the column as INTEGER PRIMARY KEY and the column will autoincrement with each new insert.
Or you use the SQL statment ALTER to change the column type to an INTEGER PRIMARY KEY after the fact, but if your creating the tables yourself, it's best to do it in the initial creation statement.
Simplest way — Just export and re-import
It is possible, and relatively easy. Export the database as an sql file. Alter the SQL file and re-import:
sqlite3 mydata.db .dump > /tmp/backup.sql
vi /tmp/backup.sql
mv mydata.db mydata.db.old
sqlite3 mydata.db
sqlite>.read /tmp/backup.sql
You can do it with SQLite Expert Personal 4:
1) Select the table and then go to "Design" tab > "Columns" tab.
2) Click "Add" and select the new column name, and type INTEGER and Not Null > Ok.
3) Go to "Primary Key" tab in "Desgin tab". Click "Add" and select the column you just created. Check the "Autoincrement" box.
4) Click "Apply" on the right bottom part of the window.
If you go back to the "Data" tab, you will see your new column with the autogenerated numbers in it.
While the Sqlite site gives you an example how to do it with a table with only a three fields, it gets nasty with one of 30 fields. Given you have a table called OldTable with many fields, the first of which is "ID" filled with integers.
Make a copy of your database for backup.
Using the command program dot commands,
.output Oldtable.txt
.dump Oldtable
Drop Table Oldtable;
Open Oldtable.txt in Microsoft Word or a grep like text editor. Find and Replace your Integer field elements with NULL.(You may need to adjust this to fit your fields). Edit the Create Table line so the field that was defined as Integer is now INTEGER PRIMARY KEY AUTOINCREMENT.
Save as NewTable.txt
Back in the command program dot
.read NewTable.txt
Done.
ID is now autoincrement.
Yes
Do you have phpmyadmin installed? I believe if you go to the 'structure' tab and look along the right columnn (where the field types are listed) - I think you can change a setting there to make it autoincrement. There is also a SQL query that will do the same thing.
You cannot alter columns on a SQLite table after it has been created. You also cannot alter a table to add an integer primary key to it.
You have to add the integer primary key when you create the table.
Yes, you can make a column which is autoincrement. Modify the table and add a column. Keep in mind that it is of type INTEGER Primary Key.
you can alter the table, altering the column definition
Simple Answer is as below,
CREATE TABLE [TEST] (
[ID] INTEGER PRIMARY KEY AUTOINCREMENT,
[NAME] VARCHAR(100));
and you are done.

Resources