How to fix ERROR 1368: CHECK OPTION on non-updatable view in MySQL

MySQLINTERMEDIATEMEDIUMUpdated Dec 25, 2025· 115 views

This error occurs when you try to create or update a view with the WITH CHECK OPTION clause, but the view definition makes it non-updatable. Views with aggregate functions, UNION, GROUP BY, or non-mergeable joins cannot use CHECK OPTION.

Quick fix

Fixes 1368

SELECT TABLE_NAME, IS_UPDATABLE
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA = 'your_database_name'
  AND TABLE_NAME = 'your_view_name';
  1. Verify which constructs make your view non-updatable
  2. Remove WITH CHECK OPTION if the view cannot be updatable
  3. Redesign the view to remove non-updatable constructs
  4. Use INNER JOIN instead of LEFT/RIGHT JOIN
mysql — error
SELECT TABLE_NAME, IS_UPDATABLE
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA = 'your_database_name'
AND TABLE_NAME = 'your_view_name';
ERROR 1368: CHECK OPTION on non-updatable view

What this error means

MySQL views can be updatable, allowing INSERT, UPDATE, and DELETE operations through them. The WITH CHECK OPTION clause adds an additional constraint—it ensures that rows modified through the view remain visible after the modification (they still match the view's WHERE clause). However, ERROR 1368 occurs when you attempt to add a CHECK OPTION to a view that MySQL cannot update. This happens because the view's SELECT statement contains operations that break the one-to-one relationship between view rows and base table rows. The error message format is: "ERROR 1368 (HY000): CHECK OPTION on non-updatable view 'database.view_name'" This is a view definition error, not a query error. It happens at CREATE or ALTER VIEW time, not when you query the view.

Typical symptoms

  • CREATE VIEW or ALTER VIEW statement fails with ERROR 1368
  • Error occurs specifically when WITH CHECK OPTION or WITH CASCADED CHECK OPTION is included
  • Views work fine without the CHECK OPTION clause
  • Error message explicitly mentions the view name
  • Application cannot enforce row-level constraints through views

Common causes

  1. The view contains aggregate functions (SUM, COUNT, MIN, MAX, AVG, etc.)
  2. The view uses DISTINCT in the SELECT clause
  3. The view contains a GROUP BY clause
  4. The view contains a HAVING clause
  5. The view uses UNION or UNION ALL of multiple queries
  6. The view is defined with ALGORITHM = TEMPTABLE (creates a temporary non-updatable view)
  7. The view contains a subquery in the SELECT or WHERE clause that references tables from the outer query
  8. The view joins with another non-updatable view
  9. The view uses an outer join (LEFT JOIN, RIGHT JOIN) in the FROM clause
  10. The view references multiple tables without proper inner join structure

How to fix "ERROR 1368: CHECK OPTION on non-updatable view"

1Verify which constructs make your view non-updatable

First, identify what's preventing the view from being updatable. Use INFORMATION_SCHEMA to check:

sql
SELECT TABLE_NAME, IS_UPDATABLE
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA = 'your_database_name'
  AND TABLE_NAME = 'your_view_name';

If the view shows IS_UPDATABLE = NO, it cannot use CHECK OPTION.

Common non-updatable constructs in the view definition:

sql
-- Non-updatable: has aggregate function
CREATE VIEW non_updatable_view AS
SELECT department, COUNT(*) as emp_count, SUM(salary) as total_salary
FROM employees
GROUP BY department;

-- Non-updatable: has DISTINCT
CREATE VIEW distinct_view AS
SELECT DISTINCT city
FROM employees;

-- Non-updatable: has UNION
CREATE VIEW union_view AS
SELECT id, name FROM employees
UNION
SELECT id, name FROM contractors;

-- Non-updatable: has subquery in SELECT
CREATE VIEW subquery_view AS
SELECT id, name, (SELECT COUNT(*) FROM orders WHERE orders.emp_id = employees.id) as order_count
FROM employees;
2Remove WITH CHECK OPTION if the view cannot be updatable

The simplest solution: if you don't actually need to update through the view, remove the CHECK OPTION clause.

Before (fails):

sql
CREATE VIEW sales_summary AS
SELECT
  department,
  COUNT(*) as total_sales,
  SUM(amount) as total_revenue
FROM orders
GROUP BY department
WITH CHECK OPTION;  -- ERROR 1368!

After (works):

sql
CREATE VIEW sales_summary AS
SELECT
  department,
  COUNT(*) as total_sales,
  SUM(amount) as total_revenue
FROM orders
GROUP BY department;
-- No WITH CHECK OPTION needed since this is for reporting only

This approach is best for reporting views or complex analytical queries that users shouldn't modify through the view anyway.

3Redesign the view to remove non-updatable constructs

If you need the view to be updatable, remove the problematic constructs from the SELECT statement.

Remove GROUP BY and aggregate functions:

sql
-- Non-updatable (has GROUP BY and COUNT)
CREATE VIEW order_summary AS
SELECT product_id, COUNT(*) as num_orders
FROM orders
GROUP BY product_id
WITH CHECK OPTION;  -- ERROR 1368

-- Solution: Create an updatable view instead
CREATE VIEW active_orders AS
SELECT id, product_id, amount, status
FROM orders
WHERE status = 'active'
WITH CHECK OPTION;  -- Now works!

Remove DISTINCT:

sql
-- Non-updatable
CREATE VIEW unique_cities AS
SELECT DISTINCT city
FROM employees
WITH CHECK OPTION;  -- ERROR 1368

-- Solution: Select all columns (may need additional column for identity)
CREATE VIEW employee_locations AS
SELECT id, city
FROM employees
WITH CHECK OPTION;  -- Works

Avoid subqueries in SELECT:

sql
-- Non-updatable (subquery in SELECT)
CREATE VIEW emp_with_count AS
SELECT id, name, (SELECT COUNT(*) FROM orders WHERE orders.emp_id = employees.id) as order_count
FROM employees
WITH CHECK OPTION;  -- ERROR 1368

-- Solution: If you need this data, query the tables separately
-- Or create a trigger to maintain a denormalized column
4Use INNER JOIN instead of LEFT/RIGHT JOIN

Outer joins can prevent views from being updatable when updating certain columns. Use INNER JOIN when possible.

Non-updatable (outer join):

sql
CREATE VIEW employee_departments AS
SELECT e.id, e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
WITH CHECK OPTION;  -- May fail depending on structure

Updatable (inner join):

sql
CREATE VIEW employee_departments AS
SELECT e.id, e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id
WITH CHECK OPTION;  -- More likely to work

Important: You can only update columns from the main table in the FROM clause when using joins. You cannot update columns from joined tables through the view.

5Check the view algorithm and specify MERGE if needed

MySQL views can use different algorithms: MERGE or TEMPTABLE. The TEMPTABLE algorithm creates temporary non-updatable views.

Non-updatable (TEMPTABLE algorithm):

sql
CREATE ALGORITHM = TEMPTABLE VIEW my_view AS
SELECT * FROM base_table
WITH CHECK OPTION;  -- ERROR 1368

Updatable (MERGE algorithm):

sql
CREATE ALGORITHM = MERGE VIEW my_view AS
SELECT * FROM base_table
WHERE active = 1
WITH CHECK OPTION;  -- Works

Or let MySQL decide (recommended):

sql
CREATE VIEW my_view AS
SELECT * FROM base_table
WHERE active = 1
WITH CHECK OPTION;  -- MySQL automatically uses MERGE if possible

Check which algorithm your view is using:

sql
SELECT TABLE_NAME, DEFINER, SECURITY_TYPE, VIEW_DEFINITION
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA = 'your_database'
  AND TABLE_NAME = 'your_view_name';
6Use triggers for complex validation instead of CHECK OPTION

If you need row-level validation but cannot make the view updatable, use INSTEAD OF triggers instead.

Trigger approach (supports complex logic):

sql
-- Create the base view (without CHECK OPTION)
CREATE VIEW order_details_view AS
SELECT id, product_id, quantity, price, created_at
FROM order_items;

-- Create INSTEAD OF INSERT trigger
DELIMITER //
CREATE TRIGGER order_items_insert
INSTEAD OF INSERT ON order_details_view
FOR EACH ROW
BEGIN
  -- Validation logic
  IF NEW.quantity <= 0 THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = 'Quantity must be greater than 0';
  END IF;

  IF NEW.price < 0 THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = 'Price cannot be negative';
  END IF;

  -- Insert the validated data
  INSERT INTO order_items (product_id, quantity, price, created_at)
  VALUES (NEW.product_id, NEW.quantity, NEW.price, NEW.created_at);
END //
DELIMITER ;

-- Create INSTEAD OF UPDATE trigger
DELIMITER //
CREATE TRIGGER order_items_update
INSTEAD OF UPDATE ON order_details_view
FOR EACH ROW
BEGIN
  IF NEW.quantity <= 0 THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = 'Quantity must be greater than 0';
  END IF;

  UPDATE order_items
  SET quantity = NEW.quantity, price = NEW.price
  WHERE id = NEW.id;
END //
DELIMITER ;

Now you can INSERT and UPDATE through the view, with custom validation logic.

7Test the corrected view definition

After making changes, test that the view works correctly and can be updated.

Test the view creation:

sql
-- Drop the old view if it exists
DROP VIEW IF EXISTS your_view_name;

-- Create the corrected view
CREATE VIEW your_view_name AS
SELECT id, name, status
FROM base_table
WHERE status != 'deleted'
WITH CHECK OPTION;

-- Verify updatability
SELECT IS_UPDATABLE
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'your_view_name';
-- Should return 'YES'

Test INSERT through the view:

sql
INSERT INTO your_view_name (id, name, status)
VALUES (1, 'Test Record', 'active');

Test UPDATE through the view:

sql
UPDATE your_view_name
SET name = 'Updated Name'
WHERE id = 1;

Test that CHECK OPTION is enforced (should fail):

sql
-- This should fail because it violates the WHERE clause
UPDATE your_view_name
SET status = 'deleted'
WHERE id = 1;
-- With CHECK OPTION, this fails because the row would become invisible

If all tests pass, the view is now properly configured.

Advanced notes

### Understanding View Updatability

A MySQL view is updatable only if there's a one-to-one relationship between rows in the view and rows in the underlying table(s). This is a fundamental requirement because UPDATE and DELETE operations need to identify exactly which base table rows to modify.

Non-updatable constructs and why:

1. Aggregate functions (COUNT, SUM, etc.) - Multiple rows collapse into one result row. Can't update a collapsed row back to multiple rows.

2. GROUP BY clause - Multiple base rows map to one group. The reverse mapping is ambiguous.

3. HAVING clause - Further filters groups. Violates the one-to-one mapping.

4. DISTINCT - Removes duplicate rows. The distinct result might represent multiple base rows.

5. UNION - Combines results from multiple queries. Can't determine which source table to update.

6. Subqueries in SELECT - Adds computed columns that don't map to base table columns.

7. TEMPTABLE algorithm - Creates temporary results that aren't directly connected to base tables.

### LOCAL vs CASCADED CHECK OPTION

When a view is based on another view, CHECK OPTION behavior differs:

sql
-- View 1: Basic table view
CREATE VIEW v1 AS
SELECT * FROM employees
WHERE department = 'Sales'
WITH CHECK OPTION;

-- View 2: View based on view
CREATE VIEW v2 AS
SELECT * FROM v1
WHERE salary > 50000
WITH LOCAL CHECK OPTION;  -- Only checks v2's WHERE, not v1's

- WITH LOCAL CHECK OPTION: Only enforces the current view's WHERE clause
- WITH CASCADED CHECK OPTION (default): Enforces all parent view conditions too

sql
CREATE VIEW v2 AS
SELECT * FROM v1
WHERE salary > 50000
WITH CASCADED CHECK OPTION;  -- Checks both v2 and v1's conditions

### Performance Considerations

- Updatable views with CHECK OPTION are slightly slower than direct table updates because MySQL must re-check the WHERE clause after modifications
- Non-updatable reporting views have no performance penalty
- For high-frequency updates, consider application-level validation instead of CHECK OPTION

### Migration from ERROR 1368

If you have existing code that relies on updatable views with CHECK OPTION:

1. Identify which columns actually need updates - Don't include unnecessary columns in the view
2. Consider if aggregate views need updates - Usually they're read-only; use base table updates instead
3. Split into separate views - Keep read-only aggregate views separate from updatable detail views
4. Use stored procedures - For complex update logic that involves multiple tables

Example migration:

sql
-- Before: Trying to update aggregated data
CREATE VIEW monthly_sales AS
SELECT product_id, COUNT(*) as num_sales, SUM(amount) as total
FROM sales
GROUP BY product_id
WITH CHECK OPTION;  -- ERROR 1368

-- After: Separate read-only reporting view and updatable transaction view
CREATE VIEW sales_transactions AS
SELECT id, product_id, amount, sale_date, created_at
FROM sales
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
WITH CHECK OPTION;

-- Reporting view (read-only)
CREATE VIEW monthly_sales_report AS
SELECT product_id, COUNT(*) as num_sales, SUM(amount) as total
FROM sales
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY product_id;

-- Updates go through the transaction view or directly to the table

Frequently asked questions

What does "1368" mean?
MySQL views can be updatable, allowing INSERT, UPDATE, and DELETE operations through them. The WITH CHECK OPTION clause adds an additional constraint—it ensures that rows modified through the view remain visible after the modification (they still match the view's WHERE clause). However, ERROR 1368 occurs when you attempt to add a CHECK OPTION to a view that MySQL cannot update.
How do I fix "1368"?
This error occurs when you try to create or update a view with the WITH CHECK OPTION clause, but the view definition makes it non-updatable. Views with aggregate functions, UNION, GROUP BY, or non-mergeable joins cannot use CHECK OPTION.
What causes "1368"?
The view contains aggregate functions (SUM, COUNT, MIN, MAX, AVG, etc.) The view uses DISTINCT in the SELECT clause The view contains a GROUP BY clause
Is the "1368" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading