SQL Lesson 9: Working With Dates

📎 This article includes 2 downloadable practice files ↓

⏱ 3 min read

📘 SQL Beginner Course · Lesson 9 of 12

In this article
  1. Store dates as dates
  2. Date ranges: half-open is safest
  3. Pulling parts out
  4. Adding days and differences
  5. Indian financial-year quarter
  6. Where beginners go wrong
  7. Practice

Most business questions are about time: this month, last quarter, overdue by 30 days. Date handling is also where SQL dialects differ most, so this lesson shows SQLite (the practice database) with equivalents for the big two.

Store dates as dates

SQLite stores dates as text in ISO format YYYY-MM-DD, which sorts and compares correctly. Other databases have real DATE types. Never store ’04/10/2026′ text; it can’t be compared or sorted reliably.

Date ranges: half-open is safest

SELECT COUNT(*) FROM orders
WHERE order_date >= '2026-08-01' AND order_date < '2026-09-01';

“From the 1st, up to but not including the next month’s 1st” works whether the column holds dates or date-times. BETWEEN ‘2026-08-01’ AND ‘2026-08-31’ misses times on the 31st.

Pulling parts out

Want SQLite SQL Server PostgreSQL
Year strftime(‘%Y’, d) YEAR(d) EXTRACT(YEAR FROM d)
Month ‘YYYY-MM’ strftime(‘%Y-%m’, d) FORMAT(d,’yyyy-MM’) to_char(d,’YYYY-MM’)
Weekday strftime(‘%w’, d) (0=Sun) DATEPART(weekday, d) EXTRACT(DOW FROM d)
Today date(‘now’) CAST(GETDATE() AS date) CURRENT_DATE

Adding days and differences

SELECT order_id, order_date, date(order_date, '+30 days') AS due_date FROM orders;    -- SQLite
-- SQL Server: DATEADD(day, 30, order_date)    PostgreSQL: order_date + INTERVAL '30 days'

SELECT julianday(MAX(order_date)) - julianday(MIN(order_date)) AS days FROM orders;  -- SQLite
-- SQL Server: DATEDIFF(day, MIN(order_date), MAX(order_date))   PostgreSQL: MAX(d) - MIN(d)

Month-end in SQLite: date(d, 'start of month', '+1 month', '-1 day').

Indian financial-year quarter

Q1 is April–June. From the month number m: ((m + 8) % 12) / 3 + 1.

SELECT strftime('%Y-%m', order_date) AS month,
       'Q' || ((CAST(strftime('%m', order_date) AS INTEGER) + 8) % 12 / 3 + 1) AS fy_quarter,
       COUNT(*) AS orders
FROM orders GROUP BY month;

FY start year: year - (month < 4), the same trick as in Excel.

💡 Big reports often use a calendar table (one row per date with month, FY, quarter, holiday flag) and JOIN to it. It’s simpler than repeating date formulas everywhere.

Where beginners go wrong

Mistake Fix
BETWEEN with date-times Half-open range: >= start AND < next
Dates stored as dd/mm/yyyy text Convert to ISO
Copying SQL Server date code into SQLite Check the dialect’s functions
Wrapping the column in a function in WHERE Prevents index use on big tables; compare the raw column to a range

Practice

Download shop.db and this lesson’s exercise file below. Open the database in the free DB Browser for SQLite (or sqlite3 shop.db), paste the exercises into the Execute SQL tab and write a query for each task. The expected result is printed under every task; answers are at the bottom of the file.

📎 Practice files for this article

  • 📄
    Practice database (SQLite)shop.db: 12 customers, 10 products and 300 orders (April-September 2026).
    ⬇ DB · 28 KB
  • 🗃️
    Lesson 9 exercisesTasks with the expected result under each, and answers at the bottom.
    ⬇ SQL · 2 KB

Free to use for learning. Files with macros (.bas) are plain text — import them with Alt+F11 → File → Import File, and always test on a copy.

✨ Ask AI about this article

Stuck on a step? Ask a question and the AI answers using this article.

Free · AI can be wrong