SQL Query Builder

Build SELECT statements with columns, tables, WHERE conditions, ORDER BY, GROUP BY, HAVING, and LIMIT clauses.

Back to all tools on ToolForge

More in Developer Tools









SQL Output

About SQL Query Builder

This SQL query builder constructs SELECT statements with proper clause ordering and syntax. It supports column selection, table specification, WHERE filtering, ORDER BY sorting, and LIMIT/OFFSET pagination. The tool generates standard SQL compatible with MySQL, PostgreSQL, SQLite, and most relational databases.

It is useful for rapid query prototyping, generating admin panel queries, creating documentation examples, teaching SQL syntax, debugging complex queries, and building starter SQL for applications and reports.

SQL SELECT Statement Structure

A complete SELECT statement follows this clause order:

Standard SELECT Syntax:

SELECT [DISTINCT] column1, column2, ...
FROM table_name
  [JOIN other_table ON condition]
  [WHERE row_filter]
  [GROUP BY column]
  [HAVING group_filter]
  [ORDER BY column [ASC|DESC]]
  [LIMIT n [OFFSET m]]

Clause Execution Order:
1. FROM (including JOINs) - determine source data
2. WHERE - filter individual rows
3. GROUP BY - aggregate into groups
4. HAVING - filter groups
5. SELECT - choose columns
6. ORDER BY - sort results
7. LIMIT/OFFSET - paginate output

Note: This tool builds the core clauses.
JOIN syntax requires manual addition for complex queries.

SELECT Clause Examples

Select all columns:
  SELECT * FROM users;

Select specific columns:
  SELECT id, username, email FROM users;

Select with aliases:
  SELECT
    id AS user_id,
    username AS user_name,
    email AS user_email
  FROM users;

Select with DISTINCT:
  SELECT DISTINCT country FROM customers;

Select with expressions:
  SELECT
    first_name || ' ' || last_name AS full_name,
    age,
    age * 12 AS age_in_months
  FROM users;

Select with aggregate functions:
  SELECT
    COUNT(*) AS total_users,
    AVG(age) AS avg_age,
    MAX(created_at) AS latest_signup
  FROM users;

WHERE Clause Operators

Operator Description Example
= Equals WHERE status = 'active'
<>, != Not equals WHERE role <> 'admin'
<, >, <=, >= Comparison WHERE age >= 18
BETWEEN Range WHERE price BETWEEN 10 AND 50
IN Set membership WHERE status IN ('active', 'pending')
LIKE Pattern match WHERE name LIKE 'A%'
IS NULL Null check WHERE deleted_at IS NULL
IS NOT NULL Not null WHERE email IS NOT NULL

ORDER BY Examples

Single column ascending:
  ORDER BY name ASC

Single column descending:
  ORDER BY created_at DESC

Multiple columns:
  ORDER BY last_name ASC, first_name ASC

Mixed sort directions:
  ORDER BY status DESC, created_at ASC

With expressions:
  ORDER BY price * quantity DESC

With column position (not recommended):
  ORDER BY 2, 1 DESC

NULL handling (PostgreSQL):
  ORDER BY created_at DESC NULLS LAST

NULL handling (MySQL):
  ORDER BY ISNULL(created_at), created_at DESC

LIMIT and OFFSET for Pagination

First 10 rows:
  LIMIT 10

Rows 11-20 (page 2, 10 per page):
  LIMIT 10 OFFSET 10

Rows 21-30 (page 3, 10 per page):
  LIMIT 10 OFFSET 20

Pagination formula (page N, page_size P):
  LIMIT P OFFSET (N-1)*P

Example: Page 5 with 20 items per page
  LIMIT 20 OFFSET 80

PostgreSQL/SQLite alternative syntax:
  LIMIT 10 OFFSET 20
  -- or --
  OFFSET 20 FETCH NEXT 10 ROWS ONLY

SQL Server syntax:
  OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY

Oracle 12c+ syntax:
  OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY

MySQL/PostgreSQL traditional:
  LIMIT 20, 10  -- OFFSET, LIMIT

GROUP BY and Aggregation

GROUP BY groups rows for aggregate calculations:

Count by status:
  SELECT status, COUNT(*) AS count
  FROM orders
  GROUP BY status;

Sum by category:
  SELECT category, SUM(price) AS total
  FROM products
  GROUP BY category;

Multiple aggregates:
  SELECT
    department,
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary,
    MIN(salary) AS min_salary
  FROM employees
  GROUP BY department;

Group by multiple columns:
  SELECT
    YEAR(order_date) AS year,
    MONTH(order_date) AS month,
    SUM(total) AS monthly_total
  FROM orders
  GROUP BY YEAR(order_date), MONTH(order_date);

HAVING to filter groups:
  SELECT department, AVG(salary) AS avg_sal
  FROM employees
  GROUP BY department
  HAVING AVG(salary) > 50000;

Complete Query Examples

Example 1: Active Users List
  SELECT id, username, email, created_at
  FROM users
  WHERE status = 'active'
  ORDER BY created_at DESC
  LIMIT 100;

Example 2: Recent Orders with Total
  SELECT
    order_id,
    customer_id,
    order_date,
    total_amount
  FROM orders
  WHERE order_date >= '2024-01-01'
    AND status IN ('pending', 'shipped')
  ORDER BY order_date DESC
  LIMIT 50;

Example 3: Product Sales Summary
  SELECT
    p.category,
    COUNT(*) AS units_sold,
    SUM(oi.quantity) AS total_quantity,
    SUM(oi.quantity * oi.price) AS revenue
  FROM order_items oi
  JOIN products p ON oi.product_id = p.id
  WHERE oi.order_date BETWEEN '2024-01-01' AND '2024-12-31'
  GROUP BY p.category
  HAVING SUM(oi.quantity * oi.price) > 1000
  ORDER BY revenue DESC;

Example 4: User Activity Report
  SELECT
    u.id,
    u.username,
    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
  WHERE u.created_at >= '2023-01-01'
  GROUP BY u.id, u.username
  HAVING COUNT(o.id) > 0
  ORDER BY total_spent DESC
  LIMIT 100;

SQL JOIN Types Reference

JOIN Type Description Use Case
INNER JOIN Returns matching rows only Orders with valid customers
LEFT JOIN All left rows + matched right All users + their orders (if any)
RIGHT JOIN All right rows + matched left All orders + customer info (if exists)
FULL OUTER JOIN All rows from both tables Reconcile two data sources
CROSS JOIN Cartesian product Generate all combinations

SQL Dialect Differences

LIMIT/OFFSET Syntax:
  MySQL/PostgreSQL:  LIMIT 10 OFFSET 20
  PostgreSQL:        LIMIT 10 OFFSET 20
  SQL Server:        OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
  Oracle 12c+:       OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY

String Concatenation:
  Standard SQL:      'Hello' || ' ' || 'World'
  MySQL:             CONCAT('Hello', ' ', 'World')
  SQL Server:        'Hello' + ' ' + 'World'

Boolean Values:
  PostgreSQL:        is_active = TRUE
  MySQL:             is_active = 1
  SQL Server:        is_active = 1
  Oracle:            is_active = 1

Date Functions:
  PostgreSQL:        NOW(), CURRENT_DATE
  MySQL:             NOW(), CURDATE()
  SQL Server:        GETDATE(), CAST(GETDATE() AS DATE)
  Oracle:            SYSDATE, TRUNC(SYSDATE)

Top N Records:
  SQL Server:        SELECT TOP 10 * FROM users
  Oracle:            SELECT * FROM users WHERE ROWNUM <= 10
  MySQL/PostgreSQL:  SELECT * FROM users LIMIT 10

Query Optimization Tips

Common SQL Patterns

Pattern SQL Example
Soft delete WHERE deleted_at IS NULL
Pagination ORDER BY id DESC LIMIT 20 OFFSET 0
Search with wildcard WHERE name LIKE '%keyword%'
Date range WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'
Multi-value filter WHERE status IN ('active', 'pending')
Exclude NULLs WHERE email IS NOT NULL
Aggregation filter GROUP BY category HAVING COUNT(*) > 10

Frequently Asked Questions

What is the standard SQL SELECT statement syntax?
The standard SQL SELECT syntax is: SELECT [DISTINCT] column1, column2, ... FROM table_name [WHERE condition] [GROUP BY column] [HAVING condition] [ORDER BY column [ASC|DESC]] [LIMIT n]. The clauses must appear in this specific order. SELECT and FROM are required; other clauses are optional.
What is the difference between WHERE and HAVING?
WHERE filters rows before grouping; HAVING filters groups after aggregation. WHERE cannot use aggregate functions (COUNT, SUM, AVG); HAVING is designed for them. Example: WHERE salary > 50000 filters individual employees; HAVING AVG(salary) > 50000 filters departments by average salary.
How does ORDER BY work with multiple columns?
Multiple ORDER BY columns are sorted hierarchically: ORDER BY last_name ASC, first_name ASC sorts by last name first, then first name within each last name. Each column can independently specify ASC (ascending) or DESC (descending). NULL values sort first in ASC, last in DESC by default (varies by database).
What is the purpose of DISTINCT in SELECT?
DISTINCT eliminates duplicate rows from results. SELECT DISTINCT country returns each country once even if multiple customers share it. DISTINCT applies to all selected columns combined: SELECT DISTINCT city, country returns unique (city, country) pairs. DISTINCT can impact performance on large datasets.
How do LIMIT and OFFSET work together?
LIMIT restricts the number of rows returned; OFFSET skips rows before starting to return. LIMIT 10 OFFSET 20 returns rows 21-30 (0-indexed: rows 20-29). This enables pagination: page 1 = LIMIT 10 OFFSET 0, page 2 = LIMIT 10 OFFSET 10. Note: OFFSET can be slow on large tables.
What are SQL JOIN types and when to use them?
INNER JOIN returns matching rows from both tables. LEFT JOIN (LEFT OUTER JOIN) returns all left table rows plus matched right table rows (NULL if no match). RIGHT JOIN is the reverse of LEFT JOIN. FULL OUTER JOIN returns all rows from both tables. CROSS JOIN produces Cartesian product. Use INNER JOIN for mandatory relationships, LEFT JOIN for optional relationships.