Real-world data is messy and often needs careful formatting. Date and String functions allow you to extract precise information from timestamps, clean up text data, and perform advanced time-series analysis.
Different databases have different syntax for date extraction, but most support some version of EXTRACT or string manipulation functions like SUBSTR.
-- Monthly order trends
SELECT
SUBSTR(CAST(order_purchase_timestamp AS VARCHAR), 1, 7) AS year_month,
COUNT(*) AS order_count
FROM orders
GROUP BY 1
ORDER BY 1;Calculating the difference between dates is essential for warehouse and logistics analysis.
-- Calculate average delivery time in days by state
SELECT
c.customer_state,
AVG(
CAST(o.order_delivered_customer_date AS DATE) -
CAST(o.order_purchase_timestamp AS DATE)
) AS avg_delivery_days
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_status = 'delivered'
AND o.order_delivered_customer_date IS NOT NULL
GROUP BY 1
ORDER BY 2;SQL provides a variety of functions for manipulating text:
UPPER/LOWER: Change case.LENGTH: Count characters.REPLACE: Substitute parts of a string.SUBSTR: Extract a portion of a string.TRIM: Remove leading/trailing whitespace.
-- Analyze review comment lengths
SELECT
review_score,
COUNT(*) AS num_reviews,
AVG(LENGTH(review_comment_message)) AS avg_comment_length
FROM order_reviews
WHERE review_comment_message IS NOT NULL
GROUP BY review_score;Use CAST or :: (in PostgreSQL) to convert data from one type to another, such as turning a number into text for concatenation.
SELECT
order_id,
'Total: $' || CAST(ROUND(price + freight_value, 2) AS TEXT) AS summary
FROM order_items
LIMIT 10;Analyzing data by day of the week or hour of the day can reveal deep insights into customer behavior.
-- Hour-of-day analysis
SELECT
SUBSTR(CAST(order_purchase_timestamp AS VARCHAR), 12, 2) AS hour_of_day,
COUNT(*) AS order_count
FROM orders
GROUP BY 1
ORDER BY 1;- What is the average delivery time (in days) for each product category? Show the top 10 slowest.
- Which month had the highest total revenue?
- Analyze the day-of-week pattern for order reviews. On which day do customers write the most reviews?
- Calculate the time between order approval and carrier pickup (in hours) by month.
Solutions
-- Exercise 1
SELECT COALESCE(t.product_category_name_english, p.product_category_name) AS category, AVG(CAST(o.order_delivered_customer_date AS DATE) - CAST(o.order_purchase_timestamp AS DATE)) AS avg_days FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id LEFT JOIN product_category_name_translation t ON p.product_category_name = t.product_category_name WHERE o.order_status = 'delivered' AND o.order_delivered_customer_date IS NOT NULL GROUP BY 1 HAVING COUNT(*) > 50 ORDER BY 2 DESC LIMIT 10;
-- Exercise 2
SELECT SUBSTR(CAST(o.order_purchase_timestamp AS VARCHAR), 1, 7) AS year_month, SUM(oi.price) FROM orders o JOIN order_items oi ON o.order_id = oi.order_id GROUP BY 1 ORDER BY 2 DESC LIMIT 1;
-- Exercise 3
SELECT CASE EXTRACT(DOW FROM CAST(review_creation_date AS TIMESTAMP)) WHEN 0 THEN 'Sun' WHEN 1 THEN 'Mon' WHEN 2 THEN 'Tue' WHEN 3 THEN 'Wed' WHEN 4 THEN 'Thu' WHEN 5 THEN 'Fri' WHEN 6 THEN 'Sat' END AS day, COUNT(*) FROM order_reviews GROUP BY EXTRACT(DOW FROM CAST(review_creation_date AS TIMESTAMP)) ORDER BY 2 DESC;
-- Exercise 4
SELECT SUBSTR(CAST(order_approved_at AS VARCHAR), 1, 7) AS year_month, AVG(EXTRACT(EPOCH FROM (CAST(order_delivered_carrier_date AS TIMESTAMP) - CAST(order_approved_at AS TIMESTAMP))) / 3600.0) AS avg_pickup_hours FROM orders WHERE order_approved_at IS NOT NULL AND order_delivered_carrier_date IS NOT NULL GROUP BY 1 ORDER BY 1;