Chapter 7: Data Manipulation

INSERT, UPDATE, DELETE, and Transactions

7.1 INSERT Basics

-- Insert a single row (named columns)
INSERT INTO customers (first_name, last_name, email, city)
VALUES ('Alice', 'Chen', 'alice@example.com', 'New York');

-- Insert multiple rows
INSERT INTO customers (first_name, last_name, email, city)
VALUES
    ('Bob',   'Smith',  'bob@example.com',   'Chicago'),
    ('Carol', 'Jones',  'carol@example.com', 'Austin'),
    ('David', 'Lee',    NULL,                  'Seattle');

-- INSERT INTO ... SELECT (copy data from one table to another)
INSERT INTO archived_orders (order_id, customer_id, order_date, total)
SELECT order_id, customer_id, order_date, total
FROM orders
WHERE order_date < '2023-01-01';

7.2 UPDATE Statements

-- Update a single column
UPDATE products
SET unit_price = 34.99
WHERE product_id = 1;

-- Update multiple columns
UPDATE products
SET
    unit_price = unit_price * 1.10,
    category   = 'Premium Hardware'
WHERE category = 'Hardware' AND unit_price > 40;

-- UPDATE with a subquery
UPDATE products
SET unit_price = (
    SELECT AVG(unit_price) FROM products
)
WHERE unit_price IS NULL;
Always include WHERE: An UPDATE without WHERE modifies every row in the table. This is the most common destructive mistake in SQL. Before running an UPDATE, test the WHERE clause with a SELECT to verify which rows will be affected.

7.3 DELETE Statements

-- Delete specific rows
DELETE FROM orders
WHERE order_date < '2020-01-01';

-- Delete all rows (keeps table structure)
DELETE FROM temp_data;

-- Delete rows based on a subquery
DELETE FROM order_items
WHERE order_id IN (
    SELECT order_id FROM orders
    WHERE status = 'cancelled'
);
DELETE vs. DROP vs. TRUNCATE: DELETE removes rows (can use WHERE, logged, can be rolled back). TRUNCATE removes all rows instantly (no WHERE, not logged in some databases, cannot roll back). DROP removes the entire table structure. In SQLite, there is no TRUNCATE; use DELETE FROM table.

7.4 UPSERT (INSERT or UPDATE)

-- SQLite: INSERT OR REPLACE
INSERT OR REPLACE INTO products (product_id, product_name, unit_price)
VALUES (1, 'Widget A v2', 32.99);

-- SQLite 3.24+: ON CONFLICT (more flexible)
INSERT INTO products (product_id, product_name, unit_price, in_stock)
VALUES (1, 'Widget A', 32.99, 200)
ON CONFLICT(product_id) DO UPDATE SET
    unit_price = excluded.unit_price,
    in_stock   = excluded.in_stock;

-- PostgreSQL: ON CONFLICT
-- INSERT INTO products (...) VALUES (...)
-- ON CONFLICT (product_id) DO UPDATE SET
--     unit_price = EXCLUDED.unit_price;

7.5 Transactions

A transaction groups multiple statements into an atomic unit: either all succeed or all fail. This prevents partial updates that leave data in an inconsistent state.

-- Transfer inventory between warehouses
BEGIN TRANSACTION;

UPDATE inventory
SET quantity = quantity - 50
WHERE warehouse_id = 1 AND product_id = 101;

UPDATE inventory
SET quantity = quantity + 50
WHERE warehouse_id = 2 AND product_id = 101;

-- If both succeed:
COMMIT;

-- If something goes wrong:
-- ROLLBACK;
ACID properties: Transactions guarantee Atomicity (all or nothing), Consistency (database stays valid), Isolation (concurrent transactions don't interfere), and Durability (committed changes survive crashes). In SQLite, each individual statement is implicitly wrapped in a transaction if you don't specify one explicitly.

7.6 Bulk Data Loading

-- SQLite: import a CSV file
.mode csv
.import sales_data.csv sales

-- PostgreSQL: COPY (fastest for large datasets)
-- COPY sales FROM '/path/to/sales_data.csv'
--     WITH (FORMAT csv, HEADER true);

-- PostgreSQL: \copy (runs client-side, no superuser needed)
-- \copy sales FROM 'sales_data.csv' WITH (FORMAT csv, HEADER true);

7.7 Data Validation

-- Check constraints prevent invalid data at insert/update time
CREATE TABLE products_v2 (
    product_id   INTEGER PRIMARY KEY,
    product_name TEXT NOT NULL,
    unit_price   REAL CHECK(unit_price > 0),
    in_stock     INTEGER DEFAULT 0 CHECK(in_stock >= 0),
    category     TEXT CHECK(category IN ('Electronics', 'Hardware', 'Accessories'))
);

-- This will fail due to CHECK constraint
INSERT INTO products_v2 (product_name, unit_price)
VALUES ('Bad Product', -5.00);
-- Error: CHECK constraint failed

Exercise 7.1

Create an inventory table and insert 10 rows. Write an UPDATE that increases the quantity of all items in the 'Electronics' category by 20%. Then write a DELETE that removes items with zero quantity.

Exercise 7.2

Write a transaction that: (1) inserts a new order into the orders table, (2) inserts two line items into order_items, and (3) decrements the in_stock count for both products. Use BEGIN, COMMIT, and test with ROLLBACK.

Exercise 7.3

Create a table with CHECK constraints that ensure: price is positive, quantity is non-negative, and status is one of 'active', 'inactive', or 'discontinued'. Test by inserting both valid and invalid rows.

Official Resources

Chapter 7 Takeaways

← Chapter 6: Window Functions Chapter 8: Database Design →