SQL Date Functions: Grouping, Filtering and Extracting Dates
Dates appear in nearly every product and analytics question: monthly revenue, users active in the last 7 days, time between events. The core ideas are the same everywhere, but MySQL and PostgreSQL name many date functions differently, so it pays to know both.
Examples run in MySQL 8 and PostgreSQL · Updated
In short
EXTRACT(MONTH FROM d)andEXTRACT(YEAR FROM d)work in both databases.- Filter ranges with
>= start AND < next_startto avoid edge cases. - Group by an extracted part to get per-month or per-year totals.
- Formatting and date arithmetic functions differ between MySQL and PostgreSQL.
Portable building blocks
EXTRACT(part FROM date) pulls out a year, month, day or hour and works the same in MySQL and PostgreSQL. Comparing dates with >= and < also works everywhere.
For ranges, prefer order_date >= '2025-02-01' AND order_date < '2025-03-01' over BETWEEN with the last day of the month: it stays correct for timestamps and month lengths.
MySQL vs PostgreSQL
Format as text: DATE_FORMAT(d, '%Y-%m') in MySQL; TO_CHAR(d, 'YYYY-MM') in PostgreSQL.
Days between dates: DATEDIFF(a, b) in MySQL; a - b in PostgreSQL.
Add an interval: DATE_ADD(d, INTERVAL 7 DAY) in MySQL; d + INTERVAL '7 days' in PostgreSQL.
Start of month: DATE_FORMAT(d, '%Y-%m-01') in MySQL; DATE_TRUNC('month', d) in PostgreSQL.
The example tables
Every example on this page uses these tables. They are small on purpose, so you can check each result by eye.
orders
| order_id | customer_id | order_date | amount |
|---|---|---|---|
| 101 | 1 | 2025-01-05 | 120 |
| 102 | 1 | 2025-01-20 | 80 |
| 103 | 2 | 2025-01-07 | 200 |
| 104 | 1 | 2025-02-02 | 150 |
| 105 | 3 | 2025-02-10 | 90 |
| 106 | 2 | 2025-02-25 | 60 |
| 107 | 3 | 2025-03-01 | 130 |
| 108 | 1 | 2025-03-03 | 40 |
employees
| emp_id | name | dept_id | salary | manager_id | hire_date |
|---|---|---|---|---|---|
| 1 | Aarav | 10 | 95000 | NULL | 2021-03-01 |
| 2 | Diya | 10 | 88000 | 1 | 2022-06-15 |
| 3 | Kabir | 10 | 72000 | 1 | 2023-01-10 |
| 4 | Meera | 20 | 64000 | NULL | 2020-11-20 |
| 5 | Rohan | 20 | 58000 | 4 | 2024-02-05 |
| 6 | Isha | 30 | 61000 | NULL | 2022-09-12 |
| 7 | Arjun | 20 | 64000 | 4 | 2023-07-30 |
| 8 | Zoya | NULL | 52000 | NULL | 2024-05-01 |
Examples
Orders and revenue per month
Group on the extracted month. Use the same expression in SELECT and GROUP BY.
SELECT EXTRACT(MONTH FROM order_date) AS month,
COUNT(*) AS orders,
SUM(amount) AS revenue
FROM orders
GROUP BY EXTRACT(MONTH FROM order_date)
ORDER BY month;| month | orders | revenue |
|---|---|---|
| 1 | 3 | 400 |
| 2 | 3 | 300 |
| 3 | 2 | 170 |
A safe date range
Everything in February 2025: on or after the 1st, and strictly before March 1st.
SELECT order_id, order_date, amount
FROM orders
WHERE order_date >= '2025-02-01' AND order_date < '2025-03-01'
ORDER BY order_date;| order_id | order_date | amount |
|---|---|---|
| 104 | 2025-02-02 | 150 |
| 105 | 2025-02-10 | 90 |
| 106 | 2025-02-25 | 60 |
Filter by year
Employees hired in 2023 or later.
SELECT name, hire_date, EXTRACT(YEAR FROM hire_date) AS hire_year
FROM employees
WHERE EXTRACT(YEAR FROM hire_date) >= 2023
ORDER BY hire_date;| name | hire_date | hire_year |
|---|---|---|
| Kabir | 2023-01-10 | 2023 |
| Arjun | 2023-07-30 | 2023 |
| Rohan | 2024-02-05 | 2024 |
| Zoya | 2024-05-01 | 2024 |
Common mistakes
- Grouping by month only. Grouping by month number merges January 2024 with January 2025. Group by year and month together when data spans years.
- BETWEEN with timestamps.
BETWEEN '2025-02-01' AND '2025-02-28'misses orders later in the day on the 28th when the column is a timestamp. - Mixing dialects.
DATEDIFFandDATE_FORMATfail in PostgreSQL;TO_CHARandDATE_TRUNCfail in MySQL. Check which database the interview uses.
Practise Date Functions
Interview questions that use this concept, easiest first. Each one has the step-by-step visualizer and answer checking.
Daily Active Users (DAU)
Compute the daily count of unique active users from raw product activity event timestamps.
Rising Temperature
Find all dates where the temperature was higher than the previous day using DATEDIFF.
Active Users in Last 7 Days
Filter rolling active accounts by comparing timestamp with CURRENT_DATE interval arithmetic.
Amazon: Late Shipments Audit
Identify orders delivered past their guaranteed delivery date and calculate the number of days late.
Listing Booking Windows
Count each listing's bookings in the last 90 days, the last 365 days and all time.
Monthly Listing Ratings
Average star rating for each listing in each month.
Want a full set? Work through the Dates & Time study plan.
Questions people ask
- How do I get the month name?
- MySQL: MONTHNAME(d) or DATE_FORMAT(d, '%M'). PostgreSQL: TO_CHAR(d, 'Month').
- How do I find activity in the last 7 days?
- MySQL: d >= CURDATE() - INTERVAL 7 DAY. PostgreSQL: d >= CURRENT_DATE - INTERVAL '7 days'.