DEV Community

Alex Chen
Alex Chen

Posted on

SQL Queries Every Developer Should Know (With Examples)

SQL Queries Every Developer Should Know (With Examples)

You don't need to be a DBA. But you DO need to write queries.

The Basics

-- SELECT: Read data
SELECT name, email FROM users WHERE active = 1;

-- INSERT: Add data
INSERT INTO users (name, email, role) VALUES ('Alex', 'alex@example.com', 'user');

-- UPDATE: Change data
UPDATE users SET role = 'admin' WHERE id = 123;

-- DELETE: Remove data
DELETE FROM users WHERE id = 123;
Enter fullscreen mode Exit fullscreen mode

Filtering and Sorting

-- WHERE clauses
SELECT * FROM products WHERE price > 100;
SELECT * FROM users WHERE name LIKE 'A%';          -- Starts with 'A'
SELECT * FROM users WHERE email LIKE '%@gmail.com'; -- Ends with @gmail.com
SELECT * FROM orders WHERE status IN ('pending', 'shipped');
SELECT * FROM users WHERE age BETWEEN 18 AND 65;
SELECT * FROM users WHERE bio IS NULL;               -- Check for NULL (not = NULL)

-- AND / OR / NOT
SELECT * FROM users WHERE active = 1 AND (role = 'admin' OR role = 'moderator');

-- ORDER BY
SELECT * FROM posts ORDER BY created_at DESC;        -- Newest first
SELECT * FROM products ORDER BY price ASC, name ASC; -- Sort by price, then name

-- LIMIT and OFFSET (pagination)
SELECT * FROM posts ORDER BY created_at DESC LIMIT 20 OFFSET 40; -- Page 3
Enter fullscreen mode Exit fullscreen mode

JOINs — The Queries That Matter Most

-- INNER JOIN: Only matching rows
SELECT 
  u.name AS user_name,
  o.total AS order_total,
  o.created_at AS order_date
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed';

-- LEFT JOIN: All users, even without orders
SELECT 
  u.name,
  COUNT(o.id) AS order_count,
  COALESCE(SUM(o.total), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC;

-- Multiple JOINs: Real-world query
SELECT 
  u.name AS customer,
  p.name AS product,
  oi.quantity,
  oi.price,
  o.created_at AS order_date
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.status = 'completed'
  AND o.created_at >= '2026-01-01'
ORDER BY o.created_at DESC;
Enter fullscreen mode Exit fullscreen mode

Aggregation — GROUP BY is Your Friend

-- Count per status
SELECT status, COUNT(*) AS count FROM orders GROUP BY status;

-- Revenue by month
SELECT 
  DATE_FORMAT(created_at, '%Y-%m') AS month,
  COUNT(*) AS orders,
  SUM(total) AS revenue,
  AVG(total) AS avg_order_value
FROM orders
WHERE status = 'completed'
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY month DESC;

-- Top customers
SELECT 
  u.name,
  u.email,
  COUNT(o.id) AS total_orders,
  SUM(o.total) AS lifetime_value
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed'
GROUP BY u.id
HAVING lifetime_value > 1000        -- Filter AFTER grouping
ORDER BY lifetime_value DESC
LIMIT 10;
Enter fullscreen mode Exit fullscreen mode

Subqueries

-- Users who haven't ordered in 90 days
SELECT name, email, last_order_date
FROM (
  SELECT 
    u.name, u.email,
    MAX(o.created_at) AS last_order_date
  FROM users u
  LEFT JOIN orders o ON u.id = o.user_id
  GROUP BY u.id
) AS user_orders
WHERE last_order_date < DATE('now', '-90 days')
   OR last_order_date IS NULL;

-- Products more expensive than average
SELECT name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products)
ORDER BY price DESC;
Enter fullscreen mode Exit fullscreen mode

Window Functions (Advanced but Essential)

-- Row numbers for ranking
SELECT 
  name,
  total_spent,
  ROW_NUMBER() OVER (ORDER BY total_spent DESC) AS rank
FROM customer_summary;

-- Running total
SELECT 
  DATE(created_at) AS day,
  SUM(total) AS daily_revenue,
  SUM(SUM(total)) OVER (ORDER BY DATE(created_at)) AS running_total
FROM orders
WHERE status = 'completed'
GROUP BY DATE(created_at)
ORDER BY day;

-- Percentile within group
SELECT 
  department,
  name,
  salary,
  PERCENT_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
FROM employees;
Enter fullscreen mode Exit fullscreen mode

Useful Utility Queries

-- Find duplicates
SELECT email, COUNT(*) AS count 
FROM users 
GROUP BY email 
HAVING COUNT(*) > 1;

-- Delete duplicates (keep latest)
DELETE FROM users
WHERE id NOT IN (
  SELECT MIN(id) FROM users GROUP BY email
);

-- Upsert (insert or update)
INSERT INTO users (id, name, email)
VALUES (1, 'Alex', 'alex@example.com')
ON CONFLICT(id) DO UPDATE SET name = excluded.name, email = excluded.email;

-- Conditional counting
SELECT 
  COUNT(*) AS total_users,
  SUM(CASE WHEN role = 'admin' THEN 1 ELSE 0 END) AS admins,
  SUM(CASE WHEN active = 1 THEN 1 ELSE 0 END) AS active_users,
  SUM(CASE WHEN created_at >= DATE('now', '-30 days') THEN 1 ELSE 0 END) AS new_users
FROM users;

-- Date arithmetic
SELECT * FROM orders 
WHERE created_at >= DATE('now', '-7 days')     -- Last 7 days
  AND created_at < DATE('now', '-1 day');      -- Before yesterday

-- String manipulation
SELECT 
  UPPER(name) AS uppercase_name,
  LOWER(email) AS lowercase_email,
  TRIM(bio) AS clean_bio,
  SUBSTR(description, 1, 100) AS short_desc,
  REPLACE(url, 'http://', 'https://') AS secure_url
FROM users;
Enter fullscreen mode Exit fullscreen mode

Performance Tips

-- EXPLAIN: See how your query executes
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';

-- Create indexes for slow queries
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
CREATE INDEX idx_posts_created ON posts(created_at DESC);

-- Common mistakes:
-- ❌ SELECT * (selects all columns — slow)
-- ✅ SELECT id, name, email (only what you need)

-- ❌ LIKE '%keyword%' (can't use index)
-- ✅ LIKE 'keyword%' (can use index for prefix)

-- ❌ Subquery in SELECT (runs for every row)
-- ✅ JOIN instead of correlated subquery

-- ❌ ORDER BY on unindexed column
-- ✅ Add index or avoid sorting huge result sets
Enter fullscreen mode Exit fullscreen mode

The Quick Reference

Query Type Example
Filter WHERE price > 100
Pattern match WHERE name LIKE 'A%'
Sort ORDER BY created_at DESC
Pagination LIMIT 20 OFFSET 40
Join INNER JOIN orders ON ...
Aggregate COUNT(*), SUM(price)
Group GROUP BY status
Having HAVING COUNT(*) > 5
Subquery WHERE price > (SELECT AVG(price) ...)
Upsert ON CONFLICT DO UPDATE
Window ROW_NUMBER() OVER (...)
Index CREATE INDEX idx ON table(col)

What's your most-used SQL query? Share it below!

Follow @armorbreak for more developer content.

Top comments (0)