SQLite vs the filesystem

Mountains

16 A little that a righteous man hath is better than the riches of many wicked.

17 For the arms of the wicked shall be broken: but the LORD upholdeth the righteous.

Psalm 37:16-17, The Bible

I know it seems like I'm beating a dead horse at this point, but I must say, I'm loving every moment of it. Not necessarily because I like to shit on SQL (though I do), but because I am currently enjoying the benefits of avoiding the language completely.

Take for instance this website. I am building it from scratch without using a single line of SQL. In fact, recently I made some changes to my website and it was relatively easy to change and test afterwards compared to SQL.

In this blog post, I would like SQLite and the filesystem to go head-to-head to see who the real champion of the database management system (DBMS) space is. I will compare the current way I'm doing things to how I would have done them with SQLite.

Without further ado, let the punching begin! 🥊

Referee's banter

Referee: Okay boys, I want a nice clean fight, and by clean, I mean foul and rough. I want no holding back. Teeth missing, ribs cracked, lungs being coughed out, the whole nine yards, capice? Let's give the audience what they asked for tonight!

Round 1: Database creation

Using SQLite:

CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT,
    category TEXT,
    content TEXT,
    banner TEXT,
    link TEXT,
    preamble TEXT,
    public INTEGER DEFAULT TRUE,
    hidden INTEGER DEFAULT FALSE,
    date_published TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)

Using the filesystem (i.e. GNU core utils):

$ mkdir -p data/posts

Some observations:

Commentator: What a blow by filesystem! SQLite did not see that swift right cross coming, but he's still on his feet!

Round winner: Filesystem

Round 2: Database population

Using SQLite:

-- After writing the blog post in 'sql-sucks.md'

INSERT INTO posts (
    title,
    category,
    content,
    banner,
    link,
    preamble
) VALUES (
    'SQL sucks!',
    'General',
    'It really does!',
    'images/sql-burning-in-fire.webp',
    '/posts/sql-sucks',
    'This is an introduction'
)

Using the filesystem:

$ # After writing the blog post in 'sql-sucks.md'
$ cp sql-sucks.md data/posts

This works because all the metadata that I would usually keep in the SQL database is at the top of the file already. This concept is so widely used in real life applications. The concept I'm referring to is the head-body concept, though that is not the official name.

The concept of that head and body parallels heaven and earth (as I mentioned in my yin-yang post), though I don't want to focus on that now. What I do want to focus on is how data in files are usually stored with some header information (i.e. head) before the content (i.e. body). We see this in music files (MP3, AAC, WMV), video files (MP4, MPEG, AVI), documents (XML, HTML, SVG) and so many other places.

Therefore, I just copy the same formula here; all Markdown files contain all their metadata. This makes it easy to query in the future, as we will see later.

Commentator: Straight punch to the gut by filesystem! SQLite is coughing out blood now!

Round winner: Filesystem

Round 3: Database query

Using SQLite:

SELECT id,
    title,
    category,
    content,
    banner,
    link,
    preamble,
    public,
    hidden,
    date_published
    FROM posts
    LIMIT 10

Querying with the filesystem is a little different. The whole purpose of using the filesystem is customization and vendor lock-in prevention. That being said, I had to build my database management system API from scratch so it took a little longer than it would take for a SQL database, admittedly. However, when I was done, I came up with this:

$ python
>>> from app.database import Database
>>> db = Database()
>>> posts = db.posts.previews.get(limit=10)
>>> posts[0].title
'SQLite vs the filesystem'

A couple things to unravel here:

Commentator: A swift uppercut from the filesystem throws SQLite back a couple steps! He is really in a daze!

Round winner: Filesystem

Round 4: Data manipulation

In SQLite:

UPDATE posts SET title = 'SQL - Stupid Query Language',
    description = 'A query language'
    WHERE id = 1;

For the filesystem, I could open any file directly and make the update. If I wanted to update multiple files, sed comes to my rescue:

$ sed -i -e "s/#\s+(.*)/SQL - Stupid Query Language/g" -e "s/^(Description:)\s+(.*)/\1\sA query language/g" blog-post-in-question.md

Using sed is just one way. There are other programs, both terminal and graphical applications, that can search through text files. So don't let the sed script scare you; the point is that you can use whatever you want since each Markdown is a plain old text file.

Commentator: Just when you thought SQLite was out of the match, he's fighting back! Way to go landing his first haymaker of the fight!

Round winner: SQLite (since it's a bit less cryptic)

Round 5: Data deletion

In SQLite:

DELETE posts WHERE id = 1;

In the filesystem:

$ rm data/posts/sql-sucks.md

Commentator: SQLite is still standing, but the filesystem is still swinging his fists!

Round winner: Filesystem

Round 6: Database alteration

Adding a column in SQLite:

ALTER TABLE posts ADD COLUMN date_updated TIMESTAMP DEFAULT NULL;

Sadly, all your information has to be uniform. You cannot have more fields for one post than others. This may be desired in some applications, but specifically with this website, I don't care to have that restriction.

For the filesystem, I just open the file in question in my favorite editor and change its metadata:

$ vim sql-sucks.md

Each column in the SQLite database would represent a metadata field in the file. In my Markdown file, I have it like this:

Title: SQL sucks
Description: The many reasons SQL sucks
Published: 2024-03-15 00:12

# SQL sucks

It really does.

So to add the field Updated, I need to add it to my file:

Title: SQL sucks
Description: The many reasons SQL sucks
Published: 2024-03-15 00:12
Updated: 2024-03-15 16:37

# SQL sucks

It really does.

Commentator: SQLite should change his name to beat, battered and bruised!

Round winner: Filesystem

Round 7: Database dump

In SQLite:

$ sqlite3 data.db .dump > dump.sql

For the filesystem, just archive your files:

$ tar -cvzf mywebsite.zip data/

A couple takeaways:

Commentator: This is training for the filesystem! SQLite won't even fight back anymore!

Round winner: Filesystem

Round 8: Database restore

In SQLite:

$ sqlite3 data.db < dump.sql

For the filesystem:

$ tar -xvf mywebsite.zip

Commentator: Uh-oh, with that last blow, SQLite is on the floor and may not be getting up anymore!

Round winner: Filesystem

Who's the champion?

Commentator: This was a tough battle, but very entertaining. I wish I could say both parties held their ground, but that was not what I saw today. SQLite was outsmarted, outperformed and outclassed. He lives to fight another day, but he may not want that day to ever come.

Bonus: more jabs at SQL (stolen from Reddit)

The bottom line

While they promise them liberty, they themselves are the servants of corruption: for of whom a man is overcome, of the same is he brought in bondage.

2 Peter 2:19, The Bible

SQL is not the worst language in the world, but it is one of the most overused. The old me would not consider any other option simply due to ignorance of other methods.

When in doubt, use the filesystem and ditch SQL. Don't mean to fat-shame but SQLite needs to lose a couple more pounds and probably drop to someone in a lower weight class.

If you would like to reply to or comment on this blog post, feel free to email me at efe@mmhq.me.