DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on

3 1

SQLite findings (on JSON and others)

JSON

Not that intuitive, but can be as powerful than MongoDB; although I am not sure how indexing work... (but it does work)

sqlite> CREATE TABLE user (phone);
sqlite> INSERT INTO user VALUES ('{}');
sqlite> CREATE UNIQUE INDEX idx_user_phone_x_y ON user(json_extract(phone, '$.x.y'));
sqlite> UPDATE user SET phone = json_set(phone, '$.x.y', 'b');
sqlite> INSERT INTO user VALUES ('{"a":"b","x":{"y":"b"}}');
Error: UNIQUE constraint failed: index 'idx_user_phone_x_y'
sqlite> UPDATE user SET phone = json_set(phone, '$.x', json('{"z":"c"}'));
Enter fullscreen mode Exit fullscreen mode

You can also declare column type as JSON, but it will not check invalid JSON for you. (Unless you put a check constraint.)

sqlite> CREATE TABLE user (name JSON);
sqlite> INSERT INTO user VALUES ('{');
sqlite> CREATE TABLE user (name CHECK(json_valid(name)));
sqlite> INSERT INTO user VALUES ('{');
Error: CHECK constraint failed: user
sqlite> INSERT INTO user VALUES ('a');
Error: CHECK constraint failed: user
sqlite> INSERT INTO user VALUES ('1');
sqlite> INSERT INTO user VALUES (1);
sqlite> INSERT INTO user VALUES ('{}');
Enter fullscreen mode Exit fullscreen mode

SQLITE_MAX_VARIABLE_NUMBER

This used to be 999 by default in older versions, but it is now increased.

It was a big limiter for me, for Python, Node.js, and Android; where I don't know how to compile the SQLite driver myself.

In Golang's go-sqlite, it is as easy as

export CGO_CFLAGS='-DSQLITE_MAX_VARIABLE_NAME=1000000'
Enter fullscreen mode Exit fullscreen mode

Type checking vs Type coercion

It does not happen, unless you create CONSTRAINT. I know there is affinity, but I am not sure what that means. (especially for "NUMERIC")

sqlite> CREATE TABLE user (name INT);
sqlite> INSERT INTO user VALUES ('a');
sqlite> INSERT INTO user VALUES (1);
sqlite> PRAGMA integrity_check;
ok
sqlite> CREATE TABLE user1 (name CHECK(TYPEOF(name) = 'integer'));
sqlite> INSERT INTO user1 VALUES ('a');
Error: CHECK contraint failed: user1
Enter fullscreen mode Exit fullscreen mode

Interestingly, SQLite have type coercion (which I believe, should be kept to minimum).

sqlite> CREATE TABLE user (name INT);
sqlite> INSERT INTO user VALUES ('1');
sqlite> INSERT INTO user VALUES (1);
sqlite> SELECT COUNT(*) FROM user WHERE name = 1;
2
sqlite> CREATE TABLE user (name);
sqlite> INSERT INTO user VALUES ('1');
sqlite> INSERT INTO user VALUES (1);
sqlite> SELECT COUNT(*) FROM user WHERE name = 1;
1
Enter fullscreen mode Exit fullscreen mode

Maybe it is similar to COLLATION?

sqlite> CREATE TABLE user (name COLLATE NOCASE);
sqlite> INSERT INTO user VALUES ('a');
sqlite> INSERT INTO user VALUES ('A');
sqlite> SELECT COUNT(*) FROM user WHERE name = 'a';
2
Enter fullscreen mode Exit fullscreen mode

TIMESTAMP

I can make updatedAt column via trigger as well, but I will tell you, what if I put type as TIMESTAMP first.

sqlite> CREATE TABLE user (name TIMESTAMP);
sqlite> INSERT INTO user VALUES (strftime('%s','now'));
sqlite> SELECT * FROM user WHERE name = 1610445191;
1610445191
sqlite> SELECT * FROM user WHERE name = '1610445191';
1610445191
sqlite> SELECT * FROM user WHERE name LIKE '161%';
1610445191
Enter fullscreen mode Exit fullscreen mode

About the trigger that I use, it's

CREATE TABLE user (name, updatedAt TIMESTAMP DEFAULT (strftime('%s','now')));
CREATE TRIGGER t_user_updatedAt BEFORE UPDATE ON user
FOR EACH ROW
WHEN NEW.updatedAt = OLD.updatedAt
BEGIN
  UPDATE user SET updatedAt = strftime('%s', 'now') WHERE ROWID = NEW.ROWID;
END;
Enter fullscreen mode Exit fullscreen mode

PRIMARY KEY

The only that is auto is INTEGER PRIMARY KEY (don't need autoincrement, and INT is not working, must be INTEGER).

sqlite> CREATE TABLE user (id INT PRIMARY KEY, name);
sqlite> INSERT INTO user VALUES (1, 1);
sqlite> INSERT INTO user VALUES (1, 1);
Error: UNIQUE constraint failed: user.id
sqlite> INSERT INTO user VALUES (null, 1);
sqlite> INSERT INTO user VALUES (null, 1);
Enter fullscreen mode Exit fullscreen mode

I have never known that PRIMARY KEY can be repeatedly NULL...

Of course,

sqlite> CREATE TABLE user (id INTEGER PRIMARY KEY, name);
sqlite> INSERT INTO user VALUES (null, 1);
sqlite> INSERT INTO user VALUES (1, 1);
Error: UNIQUE constraint failed: user.id
Enter fullscreen mode Exit fullscreen mode

BTW, you don't need explicit PRIMARY KEY. ROWID is already autogenerated. (If there is no INTEGER PRIMARY KEY ROW).

sqlite> CREATE TABLE user (id INT PRIMARY KEY, name);
sqlite> INSERT INTO user VALUES (null, 1);
sqlite> INSERT INTO user VALUES (1, 1);
sqlite> .header 1
sqlite> SELECT *, oid FROM user;
id|name|rowid
|1|1
1|1|2
sqlite> SELECT *, oid oid FROM user;
id|name|oid
|1|1
1|1|2
Enter fullscreen mode Exit fullscreen mode

This looks interesting...

Nonetheless, PRIMARY KEY is automatically unique (but accept NULL).

sqlite> CREATE TABLE user (id TEXT PRIMARY KEY, name);
sqlite> INSERT INTO user VALUES ('a', 1);
sqlite> INSERT INTO user VALUES ('a', 1);
Error: UNIQUE constraint failed: user.id
sqlite> INSERT INTO user VALUES (null, 1);
sqlite> INSERT INTO user VALUES (null, 1);
Enter fullscreen mode Exit fullscreen mode

API Trace View

How I Cut 22.3 Seconds Off an API Call with Sentry đź•’

Struggling with slow API calls? Dan Mindru walks through how he used Sentry's new Trace View feature to shave off 22.3 seconds from an API call.

Get a practical walkthrough of how to identify bottlenecks, split tasks into multiple parallel tasks, identify slow AI model calls, and more.

Read more →

Top comments (0)

Billboard image

Try REST API Generation for Snowflake

DevOps for Private APIs. Automate the building, securing, and documenting of internal/private REST APIs with built-in enterprise security on bare-metal, VMs, or containers.

  • Auto-generated live APIs mapped from Snowflake database schema
  • Interactive Swagger API documentation
  • Scripting engine to customize your API
  • Built-in role-based access control

Learn more

đź‘‹ Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay