SQL Views   «Prev  Next»
Lesson 8 Updating Table rows with views
Objective Use a view to update information in the underlying tables.

Update Information in the underlying tables of a View

Views are helpful in reporting because they can automate your queries a bit and save you the trouble of constantly resubmitting complex queries. As we saw in the last exercise, you can quickly select from the view, which can filter and combine information from more than one table:
To use a `VIEW` to update information in the underlying `Customer` (mandatory) and `Orders` (optional) tables, you must understand how updatable views work in relational databases and how the entity relationship (mandatory vs. optional) affects data manipulation.
⚙️ Scenario Summary:
  • Customer is a mandatory entity → each record in Orders must reference a Customer.
  • Orders is an optional entity → a Customer may or may not have any orders.
  • You want a single VIEW to update both.

✅ Step-by-Step Solution
  1. Create Base Tables

    CREATE TABLE Customer (
        CustomerID INT PRIMARY KEY,
        Name VARCHAR(100),
        Email VARCHAR(100)
    );
    
    CREATE TABLE Orders (
        OrderID INT PRIMARY KEY,
        CustomerID INT,
        OrderDate DATE,
        Amount DECIMAL(10, 2),
        FOREIGN KEY (CustomerID) REFERENCES Customer(CustomerID)
    );
    
  2. Create the View with LEFT JOIN

    Since Orders is optional, we use a LEFT JOIN.

    CREATE VIEW CustomerOrderView AS
    SELECT 
        c.CustomerID,
        c.Name,
        c.Email,
        o.OrderID,
        o.OrderDate,
        o.Amount
    FROM Customer c
    LEFT JOIN Orders o ON c.CustomerID = o.CustomerID;
    

  1. Make the View Updatable

    Not all views are inherently updatable. To update both Customer and Orders through the view:

    • The view must not include:
      • Aggregates
      • GROUP BY / HAVING
      • DISTINCT
      • UNION / JOIN (except simple joins — see below)
    • If needed, use INSTEAD OF triggers to enable updates.
  2. Using INSTEAD OF TRIGGERS (SQL Server / Oracle)

    For UPDATE on the View:

    CREATE TRIGGER trg_CustomerOrderView_Update
    INSTEAD OF UPDATE ON CustomerOrderView
    FOR EACH ROW
    BEGIN
      -- Update Customer
      UPDATE Customer
      SET Name = :NEW.Name,
          Email = :NEW.Email
      WHERE CustomerID = :OLD.CustomerID;
    
      -- If OrderID exists, update Order
      IF :NEW.OrderID IS NOT NULL THEN
        UPDATE Orders
        SET OrderDate = :NEW.OrderDate,
            Amount = :NEW.Amount
        WHERE OrderID = :OLD.OrderID;
      END IF;
    END;
    
  3. Insert Logic

    You can also handle INSERT logic similarly:

    CREATE TRIGGER trg_CustomerOrderView_Insert
    INSTEAD OF INSERT ON CustomerOrderView
    FOR EACH ROW
    BEGIN
      -- Insert into Customer
      INSERT INTO Customer (CustomerID, Name, Email)
      VALUES (:NEW.CustomerID, :NEW.Name, :NEW.Email);
    
      -- If order details are provided, insert into Orders
      IF :NEW.OrderID IS NOT NULL THEN
        INSERT INTO Orders (OrderID, CustomerID, OrderDate, Amount)
        VALUES (:NEW.OrderID, :NEW.CustomerID, :NEW.OrderDate, :NEW.Amount);
      END IF;
    END;
    


✅ Summary
Operation Action
View Join LEFT JOIN to include Customers even without Orders
Updates Use INSTEAD OF trigger to route updates to appropriate base tables
Inserts Use trigger to conditionally insert into Orders if data is present
Deletes (Optional) Use similar INSTEAD OF DELETE logic if deletions are needed

Implement the updatable View that joins a mandatory Customer table and an optional `Orders` table

For this example we are using SQL Server 2022, here’s how you can implement the updatable view that joins a mandatory `Customer` table and an optional `Orders` table, using INSTEAD OF triggers.
🧱 1. Define the Base Tables
CREATE TABLE Customer (
    CustomerID INT PRIMARY KEY,
    Name NVARCHAR(100),
    Email NVARCHAR(100)
);

CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT FOREIGN KEY REFERENCES Customer(CustomerID),
    OrderDate DATE,
    Amount DECIMAL(10, 2)
);

🔍 2. Create the View
Use `LEFT JOIN` so the view supports customers even if they have no orders.
CREATE VIEW CustomerOrderView AS
SELECT 
    c.CustomerID,
    c.Name,
    c.Email,
    o.OrderID,
    o.OrderDate,
    o.Amount
FROM Customer c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID;

🔧 3. Create `INSTEAD OF UPDATE` Trigger
This allows updates to either `Customer`, `Orders`, or both.
CREATE TRIGGER trg_Update_CustomerOrderView
ON CustomerOrderView
INSTEAD OF UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    -- Update Customer Info
    UPDATE c
    SET 
        c.Name = i.Name,
        c.Email = i.Email
    FROM Customer c
    JOIN inserted i ON c.CustomerID = i.CustomerID;

    -- Update Orders Info (if any)
    UPDATE o
    SET 
        o.OrderDate = i.OrderDate,
        o.Amount = i.Amount
    FROM Orders o
    JOIN inserted i ON o.OrderID = i.OrderID
    WHERE i.OrderID IS NOT NULL;
END;

➕ 4. Create `INSTEAD OF INSERT` Trigger
Supports insert into `Customer`, and optionally into `Orders`.
CREATE TRIGGER trg_Insert_CustomerOrderView
ON CustomerOrderView
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;

    -- Insert into Customer
    INSERT INTO Customer (CustomerID, Name, Email)
    SELECT CustomerID, Name, Email
    FROM inserted;

    -- Conditionally insert into Orders
    INSERT INTO Orders (OrderID, CustomerID, OrderDate, Amount)
    SELECT OrderID, CustomerID, OrderDate, Amount
    FROM inserted
    WHERE OrderID IS NOT NULL;
END;

❌ 5. (Optional) `INSTEAD OF DELETE` Trigger
To handle deletions:
CREATE TRIGGER trg_Delete_CustomerOrderView
ON CustomerOrderView
INSTEAD OF DELETE
AS
BEGIN
    SET NOCOUNT ON;

    -- Delete orders first (foreign key dependency)
    DELETE o
    FROM Orders o
    JOIN deleted d ON o.OrderID = d.OrderID;

    -- Then delete customer
    DELETE c
    FROM Customer c
    JOIN deleted d ON c.CustomerID = d.CustomerID;
END;

✅ Test Example
-- Insert customer only
INSERT INTO CustomerOrderView (CustomerID, Name, Email)
VALUES (1, 'Alice', 'alice@example.com');

-- Insert customer + order
INSERT INTO CustomerOrderView (CustomerID, Name, Email, OrderID, OrderDate, Amount)
VALUES (2, 'Bob', 'bob@example.com', 1001, '2025-06-20', 300.00);

-- Update order and customer info
UPDATE CustomerOrderView
SET Name = 'Robert', Amount = 350.00
WHERE OrderID = 1001;

📌 Notes
  • SQL Server doesn’t allow direct updates to multi-table views without INSTEAD OF triggers.
  • These triggers are powerful but must manually route data to the correct base tables.
  • Always validate with SELECT * FROM Customer and SELECT * FROM Orders after testing inserts and updates.

CREATE VIEW
MyView AS
SELECT * FROM 
CustomerInfo, PhoneNumbers 
WHERE
CustomerInfo.CustomerID = PhoneNumbers.CustomerID

Update Information in Underlying Tables

You create views as shown above and use them to update information in the underlying tables. There are rules that are enforced by your database engine, but generally speaking, views are created as either read-only or updateable. To understand the difference, you need to view this process through the eyes of your database engine. You need to understand what the engine has to do to fulfill an update request so you can to understand what makes a view updateable or read-only. To make a view updateable, you must have a SELECT statement that identifies the specific rows with which it is working. What does this mean? It means that when you send information to the database, the engine must be able to determine, very specifically, where that information should be stored.We will look at an example in the next lesson.

View Implementation, View Update, and Inline Views

The problem of efficiently implementing a view for querying is complex. Two main approaches have been suggested. One strategy, called query modification, involves modifying or transforming the view query (submitted by the user) into a query on the underlying base tables. For example, the query QV1 would be automatically modified to the following query by the DBMS:
SELECT Fname, Lname
FROM EMPLOYEE, PROJECT, WORKS_ON
WHERE Ssn=Essn AND Pno=Pnumber
AND Pname='ProductX';

Views defined via Complex Queries are time-consuming to execute
The disadvantage of this approach is that it is inefficient for views defined via complex queries that are time-consuming to execute, especially if multiple queries are going to be applied to the same view within a short period of time. The second strategy, called view materialization, involves physically creating a temporary view table when the view is first queried and keeping that table on the assumption that other queries on the view will follow. In this case, an efficient strategy for automatically updating the view table when the base tables are updated must be developed in order to keep the view up-to-date. Techniques using the concept of incremental update have been developed for this purpose, where the DBMS can determine what new tuples must be inserted, deleted, or modified in a materialized view table when a database update is applied to one of the defining base tables. The view is generally kept as a materialized (physically stored) table as long as it is being queried. If the view is not queried for a certain period of time, the system may then automatically remove the physical table and recompute it from scratch when future queries reference the view.
Updating of views is complicated and can be ambiguous. In general, an update on a view defined on a single table without any aggregate functions can be mapped to an update on the underlying base table under certain conditions. For a view involving joins, an update operation may be mapped to update operations on the underlying base relations in multiple ways. Hence, it is often not possible for the DBMS to determine which of the updates is intended. To illustrate potential problems with updating a view defined on multiple tables, consider the WORKS_ON1 view, and suppose that we issue the command to update the PNAME attribute of 'John Smith' from 'ProductX' to 'ProductY'. This view update is shown in UV1:
UV1: UPDATEWORKS_ON1
SET Pname = 'ProductY'
WHERE Lname='Smith' AND Fname='John'
AND Pname='ProductX';

SEMrush Software