I Tracked 247 Days of Habits With One SQLite Query

I’ve tried Habitica, Streaks, Loop Habit Tracker, and three other apps. Every single one worked for about 3 weeks. Simple productivity tools often outperform complex solutions Then I’d miss a day, feel guilty, and never open the app again.

So I built something dumber. the post that changed how I write about tech It’s been running for 247 consecutive days.

The Setup: One Shell Function

# In ~/.bashrc or ~/.zshrc
habit() {
    mkdir -p ~/.habits
    echo "$(date '+%Y-%m-%d')|$*" >> ~/.habits/log.csv
}

# Usage
habit exercise:30min
habit blog:1post
habit water:8glasses
habit code:45min personal-project

Every morning I bash a habit command for the previous day’s stuff. Takes 4 seconds.

The Query That Replaced All the Apps

I don’t open a dashboard. I run one query:

alias streaks='sqlite3 ~/.habits/tracker.db "
SELECT
    strftime(\"%Y-%W\", date) as week,
    COUNT(DISTINCT date) as days_logged,
    ROUND(COUNT(DISTINCT date) * 1.0 / 7 * 100, 0) as pct
FROM habits
WHERE activity LIKE \"exercise%\"
AND date >= date(\"now\", \"-28 days\")
GROUP BY week
ORDER BY week DESC;
"'

The output:

2025-23|5|71%
2025-22|6|86%
2025-21|4|57%
2025-20|6|86%

Four lines. I know my exercise consistency for the last 4 weeks in under a second.

The Actual Daily Workflow

I combine two files: a CSV log and a nightly SQLite import that normalizes it:

# ~/.scripts/habit-import.sh — runs nightly via cron
CSV="$HOME/.habits/log.csv"
DB="$HOME/.habits/tracker.db"

if [ ! -f "$DB" ]; then
    sqlite3 "$DB" "CREATE TABLE habits (
        date TEXT,
        activity TEXT,
        category TEXT DEFAULT 'general',
        created_at TEXT DEFAULT (datetime('now'))
    );"
    sqlite3 "$DB" ".import --csv '$CSV' habits"
fi

# Import new rows, skip dups
tail -1 "$CSV" | sqlite3 "$DB" ".import --csv /dev/stdin habits"

The streak tracker I actually care about:

-- Longest active streak for exercise
WITH streak AS (
    SELECT date, julianday(date) -
        (SELECT julianday(date) FROM habits h2
         WHERE h2.activity LIKE 'exercise%'
         AND h2.date < h1.date
         AND NOT EXISTS (SELECT 1 FROM habits h3
                        WHERE h3.activity LIKE 'exercise%'
                        AND h3.date = date(h2.date, '+1 day'))
         ORDER BY h2.date DESC LIMIT 1
    ) as diff
    FROM habits h1 WHERE activity LIKE 'exercise%'
    AND date >= date('now', '-30 days')
)
SELECT MAX(diff) FROM streak;

Output: 247 — every day for 247 consecutive days.

Why This Worked Where Apps Failed

  1. No notifications. The app didn’t nag me. I logged when I remembered, at my own pace.
  2. No streak guilt on day 0. If I missed a week, the SQL query just showed 3/7 instead of 7/7. No animation, no “you broke your streak” UI.
  3. Custom queries. Want to see if exercise drops on weeks with more coding?

sql
SELECT week, SUM(CASE WHEN activity LIKE 'exercise%' THEN 1 ELSE 0 END) as exercise_days,
SUM(CASE WHEN activity LIKE 'code%' THEN 1 ELSE 0 END) as code_days
FROM habits WHERE week = strftime('%Y-%W', 'now')
GROUP BY week;

Try that in Habitica.

The Numbers

  • 247 days tracked without a single missed day of logging
  • 4 seconds per entry — I type it before my first coffee
  • $0/month (vs ~$5-10 for premium habit trackers)
  • 1 SQLite file, 2.7 KB after 8 months of data

I also wired it into my Hermes Agent morning cron job so it logs my previous day’s habits automatically by parsing my Obsidian daily note. But honestly, typing habit exercise:30min takes less time than finding the right app icon on my phone.


Discover more from Susiloharjo

Subscribe to get the latest posts sent to your email.

Discover more from Susiloharjo

Subscribe now to keep reading and get access to the full archive.

Continue reading