SQL validation examples

SQL Validator Examples

Common SQL mistakes paired with the exact fix — syntax, alias scope, joins, aggregation, and window functions.

Examples use PostgreSQL syntax. The same validation rules apply across all 28 supported databases — MySQL, SQL Server, Oracle, BigQuery, Snowflake, and more.

1. Missing FROM clause

Invalid SQL input

SELECT
  user_id,
  email
users
WHERE
  is_active = true;

❌ Validation errors

SELECT
  user_id,
  email
-- 1. Missing FROM before the source table:
users
WHERE
  is_active = true;

💬 Explanation

#Explanation
1The SELECT statement is missing FROM users. PostgreSQL requires FROM to define the row source before WHERE filters are applied.

🚀 Corrected SQL query

SELECT
  user_id,
  email
FROM users
WHERE
  is_active = true;
2. Trailing comma in SELECT list

Invalid SQL input

SELECT
  product_name,
  price,
  category,
FROM
  products;

❌ Validation errors

SELECT
  product_name,
  price,
  -- 1. Trailing comma before FROM:
  category,
FROM
  products;

💬 Explanation

#Explanation
1PostgreSQL does not allow a trailing comma after the last projected column. Remove the comma after category before FROM.

🚀 Corrected SQL query

SELECT
  product_name,
  price,
  category
FROM
  products;
3. Alias used in WHERE before SELECT evaluation

Invalid SQL input

SELECT
  order_id,
  (quantity * unit_price) AS total_value
FROM
  order_details
WHERE
  total_value > 1000;

❌ Validation errors

SELECT
  order_id,
  (quantity * unit_price) AS total_value
FROM
  order_details
WHERE
  -- 1. SELECT alias is not visible in WHERE:
  total_value > 1000;

💬 Explanation

#Explanation
1WHERE runs before SELECT aliases are created, so total_value is undefined in this clause. Repeat the expression or use a subquery/CTE.

🚀 Corrected SQL query

SELECT
  order_id,
  (quantity * unit_price) AS total_value
FROM
  order_details
WHERE
  (quantity * unit_price) > 1000;
4. JOIN missing target table and alias

Invalid SQL input

SELECT
  c.customer_name,
  o.order_date
FROM
  customers c
JOIN
  ON c.id = o.customer_id;

❌ Validation errors

SELECT
  c.customer_name,
  -- 1. Alias "o" is referenced but never defined:
  o.order_date
FROM
  customers c
  -- 2. JOIN target table is missing:
JOIN
  -- 3. ON cannot appear without a valid JOIN target:
  ON c.id = o.customer_id;

💬 Explanation

#Explanation
1o.order_date references alias o, but no table was declared with alias o.
2Every JOIN must specify a table or subquery to join.
3The ON predicate is only valid after a complete JOIN target declaration, such as JOIN orders o.

🚀 Corrected SQL query

SELECT
  c.customer_name,
  o.order_date
FROM
  customers c
JOIN
  orders o
  ON c.id = o.customer_id;
5. Aggregation without GROUP BY

Invalid SQL input

SELECT
  department,
  AVG(salary)
FROM
  employees
HAVING
  department = 'Sales';

❌ Validation errors

SELECT
  -- 1. Non-aggregated department is selected with AVG without grouping:
  department,
  AVG(salary)
FROM
  employees
HAVING
  -- 2. HAVING is used to filter detail rows instead of grouped results:
  department = 'Sales';

💬 Explanation

#Explanation
1When aggregate functions are used, non-aggregated columns in SELECT must appear in GROUP BY.
2Use WHERE for row-level filtering (pre-aggregation) and HAVING for aggregate/group-level filtering (post-aggregation).

🚀 Corrected SQL query

SELECT
  department,
  AVG(salary) AS avg_salary
FROM
  employees
WHERE
  department = 'Sales'
GROUP BY
  department;
6. Invalid WHERE inside window OVER clause

Invalid SQL input

SELECT
  employee_name,
  salary,
  department,
  SUM(salary) OVER (PARTITION BY department WHERE is_manager = false ORDER BY hire_date) AS running_total
FROM
  employees;

❌ Validation errors

SELECT
  employee_name,
  salary,
  department,
  -- 1. OVER(...) does not allow WHERE:
  SUM(salary) OVER (PARTITION BY department WHERE is_manager = false ORDER BY hire_date) AS running_total
FROM
  employees;

💬 Explanation

#Explanation
1Window definitions only allow PARTITION BY, ORDER BY, and frame clauses. Put conditional logic in FILTER (WHERE ...) or a CASE expression.

🚀 Corrected SQL query

SELECT
  employee_name,
  salary,
  department,
  SUM(salary) FILTER (WHERE NOT is_manager) OVER (
    PARTITION BY department
    ORDER BY hire_date
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS running_total
FROM
  employees;
7. INSERT column/value count mismatch

Invalid SQL input

INSERT INTO users (
  username,
  email,
  is_active
)
VALUES (
  'alice',
  'alice@example.com'
);

❌ Validation errors

INSERT INTO users (
  username,
  email,
  is_active
)
VALUES (
  -- 1. Only two values for three target columns:
  'alice',
  'alice@example.com'
);

💬 Explanation

#Explanation
1The INSERT target list has three columns, but VALUES provides only two entries. PostgreSQL raises a column/value count mismatch error.

🚀 Corrected SQL query

INSERT INTO users (
  username,
  email,
  is_active
)
VALUES (
  'alice',
  'alice@example.com',
  true
);
8. CTE referenced without join target

Invalid SQL input

WITH monthly_revenue AS (
  SELECT
    date_trunc('month', order_date)::date AS sales_month,
    SUM(total_amount) AS revenue
  FROM orders
  GROUP BY 1
)
SELECT
  sales_month,
  revenue,
  region_name
FROM
  monthly_revenue
WHERE
  region_name = 'North America';

❌ Validation errors

WITH monthly_revenue AS (
  SELECT
    date_trunc('month', order_date)::date AS sales_month,
    SUM(total_amount) AS revenue
  FROM orders
  GROUP BY 1
)
SELECT
  sales_month,
  revenue,
  -- 1. region_name is not produced by monthly_revenue:
  region_name
FROM
  monthly_revenue
WHERE
  -- 2. region_name filter references an undefined column:
  region_name = 'North America';

💬 Explanation

#Explanation
1The CTE only returns sales_month and revenue; region_name is not part of its schema.
2Filters can only reference columns available from the current FROM scope. Add a join to a source that contains region_name.

🚀 Corrected SQL query

WITH monthly_revenue AS (
  SELECT
    date_trunc('month', o.order_date)::date AS sales_month,
    o.region_name,
    SUM(o.total_amount) AS revenue
  FROM orders o
  GROUP BY
    date_trunc('month', o.order_date)::date,
    o.region_name
)
SELECT
  sales_month,
  revenue,
  region_name
FROM
  monthly_revenue
WHERE
  region_name = 'North America';

Try these on your own queries

Generate, optimize, validate, explain, and format SQL with the SQLAI.ai tools — across every supported database.