Fun to Program – SQLite

Date: 2013/08/18 (initial publish), 2021/08/02 (last update)

Source: en/fun2-00018.md

Previous Post Top Next Post

TOC

This was originally written and created around 2013 and may require to be updated. (2021)

SQLite

SQLite is a lightweight relational database management system suitable for embedding into the application program by linking the SQLite library. Structured Query Language (SQL) can be used to manage the content of the database.

SQL basics

You can interactively input SQL commands to the command line after starting sqlite3 as:

$ sqlite3 test.db
SQLite version 3.7.14 2012-09-03 15:42:36
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>

Read references:

Alternatively, the GUI tool sqlitebrowser may be used, too.

Phonebook

Let’s make a batch file of SQL commands to create a phonebook database.

SQL batch file sql-1.sql:

.explain ON
.header ON
-- Generate a SQL data table: phone
create table phone(
	name string NOT NULL, 
	phone string NOT NULL);
insert into phone values('Napoleon Bonaparte', '+33-(0)1.01.01.01.01');
insert into phone values('George Washington', '+1-800-START-USA');
insert into phone values('John Adams', '+1-800-LOVE-BEER');
insert into phone values('Thomas Jefferson', '+1-800-4-FREEDOM');
-- SQLite parameter set and data dump
.width 8 20 20
.show
.schema
.dump phone
-- Select all phone table contents and display
select rowid,* from phone;

Here, “NOT NULL” is specified to avoid empty data.

Let’s create a SQL database file private.db containing a phonebook data as a phone table using sqlite3. For readability, I added “-echo” to echo SQL command to the console.

$ rm -f private.db # Initialize :-)
$ cat sql-1.sql | sqlite3 -echo private.db
.explain ON
.header ON
create table phone(
    name string NOT NULL,
    phone string NOT NULL);
insert into phone values('Napoleon Bonaparte', '+33-(0)1.01.01.01.01');
insert into phone values('George Washington', '+1-800-START-USA');
insert into phone values('John Adams', '+1-800-LOVE-BEER');
insert into phone values('Thomas Jefferson', '+1-800-4-FREEDOM');
.width 8 20 20
.show
     echo: on
  explain: on
  headers: on
     mode: explain
nullvalue: ""
   output: stdout
separator: "|"
    stats: off
    width: 8 20 20 4 4 13 2 13
.schema
CREATE TABLE phone(
    name string NOT NULL,
    phone string NOT NULL);
.dump phone
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE phone(
    name string NOT NULL,
    phone string NOT NULL);
INSERT INTO "phone" VALUES('Napoleon Bonaparte','+33-(0)1.01.01.01.01');
INSERT INTO "phone" VALUES('George Washington','+1-800-START-USA');
INSERT INTO "phone" VALUES('John Adams','+1-800-LOVE-BEER');
INSERT INTO "phone" VALUES('Thomas Jefferson','+1-800-4-FREEDOM');
COMMIT;
select rowid,* from phone;
rowid     name                  phone
--------  --------------------  --------------------
1         Napoleon Bonaparte    +33-(0)1.01.01.01.01
2         George Washington     +1-800-START-USA
3         John Adams            +1-800-LOVE-BEER
4         Thomas Jefferson      +1-800-4-FREEDOM
$ ls -l private.db
-rw-r--r-- 1 osamu osamu 2048 Aug 17 23:41 private.db

Let’s add, update, delete data in this phone table be creating a batch file sql-2.sql and executing sqlite3 with it.

$ cat sql-2.sql
.explain ON
.header ON
-- Add/remove entries in a SQL data table
insert into phone values('Tokugawa Ieyasu', '+81-(0)1-SHOUGUN1');
update phone set phone = '+1-800-BEER-BEER' where name = 'John Adams';
delete from phone where name like 'George%';
-- Select all phone table contents and display
.width 8 20 20
select rowid,* from phone;

$ cat sql-2.sql | sqlite3 -echo private.db
.explain ON
.header ON
insert into phone values('Tokugawa Ieyasu', '+81-(0)1-SHOUGUN1');
update phone set phone = '+1-800-BEER-BEER' where name = 'John Adams';
delete from phone where name like 'George%';
.width 8 20 20
select rowid,* from phone;
rowid     name                  phone
--------  --------------------  --------------------
1         Napoleon Bonaparte    +33-(0)1.01.01.01.01
3         John Adams            +1-800-BEER-BEER
4         Thomas Jefferson      +1-800-4-FREEDOM
5         Tokugawa Ieyasu       +81-(0)1-SHOUGUN1

SQL wildcard chracters used for LIKE matching

Wildcard Description
% A substitute for zero or more characters
_ A substitute for exactly one character

SQL + Python

You can manupilate SQL database via Python using the pysqlite binding. For example:

Python code example to add “Abraham Lincoln” to the phonebook.

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sqlite3 as lite
import sys

con = lite.connect('private.db')
with con:
    c = con.cursor()        
    c.execute("""insert into phone values(
        'Abraham Lincoln', '+1-800-FREEDOM-1');""")
    c.execute("""select rowid,* from phone""")
    for row in c.fetchall():
        print ("%s : Name = %s, Phone =  %s" % (row[0], row[1], row[2]))
con.commit()
con.close()

Let’s execute it.

$ ./sql-3.py
1 : Name = Napoleon Bonaparte, Phone =  +33-(0)1.01.01.01.01
3 : Name = John Adams, Phone =  +1-800-BEER-BEER
4 : Name = Thomas Jefferson, Phone =  +1-800-4-FREEDOM
5 : Name = Tokugawa Ieyasu, Phone =  +81-(0)1-SHOUGUN1
6 : Name = Abraham Lincoln, Phone =  +1-800-FREEDOM-1

NULL data

SQL command behavior for NULL data is complicated. See how SQLite behaves:

$ cat sql-4.sql | sqlite3 -echo private.db
.explain ON
.header ON
create table t1(a int, b int, c int);
insert into t1 values(1,0,0);
insert into t1 values(2,0,1);
insert into t1 values(3,1,0);
insert into t1 values(4,1,1);
insert into t1 values(5,null,0);
insert into t1 values(6,null,1);
insert into t1 values(7,null,null);
insert into t1 values(null,null,null);
insert into t1 values(null,null,null);
.schema
CREATE TABLE phone(
    name string NOT NULL,
    phone string NOT NULL);
CREATE TABLE t1(a int, b int, c int);
.width 8 8 8 40
select rowid,* from t1;
rowid     a         b         c
--------  --------  --------  ----------------------------------------
1         1         0         0
2         2         0         1
3         3         1         0
4         4         1         1
5         5                   0
6         6                   1
7         7
8
9
.width 4 8 60
select rowid,a, case when b<>0 then 1 else 0 end from t1;
rowi  a         case when b<>0 then 1 else 0 end
----  --------  ------------------------------------------------------------
1     1         0
2     2         0
3     3         1
4     4         1
5     5         0
6     6         0
7     7         0
8               0
9               0
select rowid,a+10, case when not b<>0 then 1 else 0 end from t1;
rowi  a+10      case when not b<>0 then 1 else 0 end
----  --------  ------------------------------------------------------------
1     11        1
2     12        1
3     13        0
4     14        0
5     15        0
6     16        0
7     17        0
8               0
9               0
select rowid,a+20, case when b<>0 and c<>0 then 1 else 0 end from t1;
rowi  a+20      case when b<>0 and c<>0 then 1 else 0 end
----  --------  ------------------------------------------------------------
1     21        0
2     22        0
3     23        0
4     24        1
5     25        0
6     26        0
7     27        0
8               0
9               0
select rowid,a+30, case when not (b<>0 and c<>0) then 1 else 0 end from t1;
rowi  a+30      case when not (b<>0 and c<>0) then 1 else 0 end
----  --------  ------------------------------------------------------------
1     31        1
2     32        1
3     33        1
4     34        0
5     35        1
6     36        0
7     37        0
8               0
9               0
select rowid,a+40, case when b<>0 or c<>0 then 1 else 0 end from t1;
rowi  a+40      case when b<>0 or c<>0 then 1 else 0 end
----  --------  ------------------------------------------------------------
1     41        0
2     42        1
3     43        1
4     44        1
5     45        0
6     46        1
7     47        0
8               0
9               0
select rowid,a+50, case when not (b<>0 or c<>0) then 1 else 0 end from t1;
rowi  a+50      case when not (b<>0 or c<>0) then 1 else 0 end
----  --------  ------------------------------------------------------------
1     51        1
2     52        0
3     53        0
4     54        0
5     55        0
6     56        0
7     57        0
8               0
9               0
select rowid,a+60, case b when c then 1 else 0 end from t1;
rowi  a+60      case b when c then 1 else 0 end
----  --------  ------------------------------------------------------------
1     61        1
2     62        0
3     63        0
4     64        1
5     65        0
6     66        0
7     67        0
8               0
9               0
select rowid,a+70, case c when b then 1 else 0 end from t1;
rowi  a+70      case c when b then 1 else 0 end
----  --------  ------------------------------------------------------------
1     71        1
2     72        0
3     73        0
4     74        1
5     75        0
6     76        0
7     77        0
8               0
9               0
select rowid,a+80, b*0 from t1;
rowi  a+80      b*0
----  --------  ------------------------------------------------------------
1     81        0
2     82        0
3     83        0
4     84        0
5     85
6     86
7     87
8
9
select rowid,a+90, b*c from t1;
rowi  a+90      b*c
----  --------  ------------------------------------------------------------
1     91        0
2     92        0
3     93        0
4     94        1
5     95
6     96
7     97
8
9
select rowid,a+100, b+c from t1;
rowi  a+100     b+c
----  --------  ------------------------------------------------------------
1     101       0
2     102       1
3     103       1
4     104       2
5     105
6     106
7     107
8
9
select rowid,a+110 from t1 where b<10;
rowi  a+110
----  --------
1     111
2     112
3     113
4     114
select rowid,+120 from t1 where not b>10;
rowi  +120
----  --------
1     120
2     120
3     120
4     120
select rowid,a+130 from t1 where b<10 OR c=1;
rowi  a+130
----  --------
1     131
2     132
3     133
4     134
6     136
select rowid,a+140 from t1 where b<10 AND c=1;
rowi  a+140
----  --------
2     142
4     144
select rowid,a+150 from t1 where not (b<10 AND c=1);
rowi  a+150
----  --------
1     151
3     153
5     155
select rowid,a+160 from t1 where not (c=1 AND b<10);
rowi  a+160
----  --------
1     161
3     163
5     165
select rowid,a+170 from t1 where a isnull;
rowi  a+170
----  --------
8
9
select rowid,a+180 from t1 where b isnull;
rowi  a+180
----  --------
5     185
6     186
7     187
8
9
select rowid,a+190 from t1 where c isnull;
rowi  a+190
----  --------
7     197
8
9
.width 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
select count(*), count(b), sum(b), avg(b), min(b), max(b) from t1;
count(*)  count(b)  sum(b)    avg(b)    min(b)    max(b)
--------  --------  --------  --------  --------  --------
9         4         2         0.5       0         1
select distinct b from t1;
b
--------
0
1

select b from t1 union select b from t1;
b
--------

0
1
create table t2(a int, b int unique);
insert into t2 values(1,1);
insert into t2 values(2,null);
insert into t2 values(3,null);
Error: near line 56: column b is not unique
insert into t2 values(4,1);
.schema
CREATE TABLE phone(
    name string NOT NULL,
    phone string NOT NULL);
CREATE TABLE t1(a int, b int, c int);
CREATE TABLE t2(a int, b int unique);
select rowid,* from t2;
rowid     a         b
--------  --------  --------
1         1         1
2         2
3         3
drop table t1;
drop table t2;
.schema
CREATE TABLE phone(
    name string NOT NULL,
    phone string NOT NULL);

TIP: The autogenerated “rowid” is a 64-bit signed integer key that uniquely identifies the row within its table.

Previous Post Top Next Post