Insert_data
Chapter 9: INSERT - Adding Data
Section titled “Chapter 9: INSERT - Adding Data”Inserting Data into PostgreSQL Tables
Section titled “Inserting Data into PostgreSQL Tables”9.1 INSERT Fundamentals
Section titled “9.1 INSERT Fundamentals”The INSERT statement adds new rows to a table. PostgreSQL provides multiple ways to insert data efficiently.
INSERT Statement Options ========================================================================
Start: Insert Data | ┌───────────────────┼───────────────────┐ | | | v v v ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Single Row │ │ Multi-Row │ │ FROM │ │ INSERT │ │ INSERT │ │ SELECT │ │ │ │ │ │ │ │ VALUES() │ │ VALUES(), │ │ INSERT ... │ │ │ │ () │ │ SELECT │ └─────────────┘ └─────────────┘ └─────────────┘ ========================================================================9.2 Basic INSERT Operations
Section titled “9.2 Basic INSERT Operations”-- Create table for examplesCREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(200) NOT NULL, description TEXT, price DECIMAL(10,2) NOT NULL, stock INTEGER DEFAULT 0, category VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
-- Insert a single row (all columns)INSERT INTO products (name, description, price, stock, category)VALUES ('Laptop', 'High-performance laptop', 999.99, 50, 'Electronics');
-- Insert with column list (recommended)INSERT INTO products (name, price, stock)VALUES ('Mouse', 29.99, 100);
-- Let DEFAULT handle valuesINSERT INTO products (name, price)VALUES ('Keyboard', 79.99);
-- Insert multiple rows in one statementINSERT INTO products (name, description, price, stock, category) VALUES ('Monitor', '27-inch 4K display', 399.99, 25, 'Electronics'), ('Headphones', 'Wireless noise-cancelling', 199.99, 75, 'Audio'), ('Webcam', '1080p HD webcam', 89.99, 40, 'Electronics');
-- Using DEFAULT values explicitlyINSERT INTO products VALUES (DEFAULT, 'Tablet', NULL, 299.99, DEFAULT, 'Electronics');9.3 INSERT with DEFAULT Values
Section titled “9.3 INSERT with DEFAULT Values”-- Using DEFAULT keywordINSERT INTO products (name, price)VALUES ('USB Cable', DEFAULT); -- Uses DEFAULT for all other columns
-- Using DEFAULT in multiple columnsINSERT INTO products (name, price, stock)VALUES ('Phone Case', 19.99, DEFAULT);
-- Check what happens with different definitionsCREATE TABLE examples ( id SERIAL PRIMARY KEY, required_field VARCHAR(50) NOT NULL, optional_field VARCHAR(50) DEFAULT 'default_value', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP);
-- Inserting with some defaultsINSERT INTO examples (required_field) VALUES ('Required Only');-- optional_field = 'default_value', created_at = NOW()
INSERT INTO examples (required_field, optional_field)VALUES ('Both Fields', 'Custom Value');-- optional_field = 'Custom Value'
INSERT INTO examples DEFAULT VALUES;-- Error: required_field NOT NULL!9.4 INSERT with SELECT
Section titled “9.4 INSERT with SELECT”-- Create source tableCREATE TABLE products_archive (LIKE products INCLUDING ALL);
-- Insert from SELECTINSERT INTO products_archiveSELECT * FROM products WHERE created_at < '2024-01-01';
-- Insert with transformationINSERT INTO products (name, price, stock, category)SELECT name || ' (Refurbished)', price * 0.8, stock, categoryFROM productsWHERE stock > 10;
-- Insert from multiple sourcesINSERT INTO products (name, price, stock)SELECT name, price, stock FROM new_productsUNION ALLSELECT name, price, stock FROM import_products;
-- Insert with computed valuesINSERT INTO orders (user_id, total_amount, status)SELECT id, (SELECT AVG(total_amount) FROM orders) * RANDOM() + 100, 'pending'FROM usersWHERE is_active = TRUE;
-- Insert with conditional logicINSERT INTO product_stats (product_name, status, stock_level)SELECT name, CASE WHEN stock = 0 THEN 'out_of_stock' WHEN stock < 10 THEN 'low_stock' ELSE 'in_stock' END, stockFROM products;9.5 INSERT with RETURNING
Section titled “9.5 INSERT with RETURNING”The RETURNING clause returns the inserted rows.
-- Return all columns of inserted rowINSERT INTO products (name, price, stock)VALUES ('New Product', 99.99, 50)RETURNING *;
-- Return specific columnsINSERT INTO products (name, price, stock)VALUES ('Another Product', 49.99, 25)RETURNING id, name;
-- Return with computed valuesINSERT INTO users (username, email)VALUES ('newuser', 'new@example.com')RETURNING id, username, created_at;
-- Use in application code (common patterns)-- PostgreSQL returns:-- INSERT 0 1 with data
-- Insert and get ID for foreign key relationshipINSERT INTO orders (user_id, total_amount)VALUES (1, 150.00)RETURNING id AS order_id;
-- Then use that ID:-- INSERT INTO order_items (order_id, product_id, quantity)-- VALUES (5, 10, 2);
-- Multiple rows with RETURNINGINSERT INTO products (name, price)VALUES ('Product A', 10.00), ('Product B', 20.00), ('Product C', 30.00)RETURNING id, name;
-- Bulk insert with returningINSERT INTO audit_log (action, record_id)SELECT 'created', id FROM usersRETURNING action, record_id;9.6 UPSERT (INSERT ON CONFLICT)
Section titled “9.6 UPSERT (INSERT ON CONFLICT)”Handle duplicate key violations elegantly.
-- Basic UPSERTINSERT INTO products (id, name, price, stock)VALUES (1, 'Laptop', 1099.99, 60)ON CONFLICT (id)DO UPDATE SET price = EXCLUDED.price, stock = EXCLUDED.stock;
-- UPSERT with DO NOTHINGINSERT INTO products (id, name, price)VALUES (1, 'Laptop', 1099.99)ON CONFLICT (id) DO NOTHING;
-- UPSERT with specific constraintINSERT INTO products (name, price)VALUES ('Laptop', 1099.99)ON CONFLICT (name)DO UPDATE SET price = EXCLUDED.price;
-- Using unique constraint nameINSERT INTO products (name, price)VALUES ('Mouse', 29.99)ON CONFLICT (uk_products_name)DO UPDATE SET price = EXCLUDED.price;
-- Complex UPSERT with multiple conditionsINSERT INTO users (username, email, is_active)VALUES ('john_doe', 'john@example.com', TRUE)ON CONFLICT (username)DO UPDATE SET email = EXCLUDED.email, is_active = TRUE, updated_at = CURRENT_TIMESTAMPWHERE users.is_active = FALSE;
-- UPSERT with RETURNINGINSERT INTO products (id, name, price)VALUES (1, 'Updated Product', 99.99)ON CONFLICT (id)DO UPDATE SET price = EXCLUDED.priceRETURNING id, name, price, (xmax = 0) AS inserted; -- TRUE if inserted, FALSE if updated9.7 INSERT with ON CONFLICT DO UPDATE
Section titled “9.7 INSERT with ON CONFLICT DO UPDATE”Advanced conflict resolution patterns.
-- Increment counter patternINSERT INTO counters (key, count)VALUES ('page_views', 1)ON CONFLICT (key)DO UPDATE SET count = counters.count + 1;
-- Aggregate into arrayINSERT INTO user_actions (user_id, actions)VALUES (1, ARRAY['login'])ON CONFLICT (user_id)DO UPDATE SET actions = user_actions.actions || EXCLUDED.actions;
-- Conditional upsertINSERT INTO orders (user_id, total_amount, status)VALUES (1, 150.00, 'pending')ON CONFLICT (id)DO UPDATE SET total_amount = orders.total_amount + EXCLUDED.total_amount, status = CASE WHEN orders.status = 'completed' THEN 'completed' ELSE 'pending' END;
-- Using subquery in DO UPDATEINSERT INTO order_totals (order_id, total)VALUES (1, 100.00)ON CONFLICT (order_id)DO UPDATE SET total = ( SELECT SUM(total_amount) FROM orders WHERE id = EXCLUDED.order_id);
-- Multiple conflict targetsINSERT INTO product_prices (product_id, price_type, price)VALUES (1, 'retail', 99.99)ON CONFLICT (product_id, price_type)DO UPDATE SET price = EXCLUDED.price;9.8 Bulk Insert Performance
Section titled “9.8 Bulk Insert Performance”-- Efficient multi-row INSERTINSERT INTO products (name, price, stock) VALUES ('Product 1', 10.00, 100), ('Product 2', 20.00, 200), ('Product 3', 30.00, 300), ('Product 4', 40.00, 400), ('Product 5', 50.00, 500);
-- Copy data from file (most efficient for large datasets)-- psql command:-- \copy products (name, price, stock) FROM 'products.csv' WITH (FORMAT csv)
-- Or using SQL COPYCOPY products (name, price, stock)FROM '/path/to/products.csv'WITH (FORMAT csv, HEADER true);
-- Export to fileCOPY products (name, price, stock)TO '/path/to/export.csv'WITH (FORMAT csv, HEADER true);
-- Batch inserts in application code-- Instead of:FOR product IN products LOOP INSERT INTO products VALUES (...); -- N queries!END LOOP;
-- Use batch:INSERT INTO products (name, price) VALUES (p1.name, p1.price), (p2.name, p2.price), ...; -- Single query!9.9 Handling NULL Values
Section titled “9.9 Handling NULL Values”-- Insert with NULLINSERT INTO products (name, description, price)VALUES ('Product', NULL, 10.00);
-- Use COALESCE in returningINSERT INTO products (name, description)VALUES ('Product', 'Default description')RETURNING id, COALESCE(description, 'No description') AS description;
-- Insert with ON CONFLICT handling NULLsINSERT INTO products (name, description)VALUES ('Product', NULL)ON CONFLICT (name)DO UPDATE SET description = COALESCE(EXCLUDED.description, products.description);9.10 Best Practices
Section titled “9.10 Best Practices” INSERT Best Practices ========================================================================
1. Use Multi-Row INSERT ┌──────────────────────────────────────────────────────────────┐ │ ❌ Single row per statement: │ │ INSERT INTO products (name) VALUES ('a'); │ │ INSERT INTO products (name) VALUES ('b'); │ │ │ │ ✓ Batch multiple rows: │ │ INSERT INTO products (name) VALUES ('a'), ('b'), ('c'); │ └──────────────────────────────────────────────────────────────┘
2. Use RETURNING ┌──────────────────────────────────────────────────────────────┐ │ • Get inserted IDs without additional query │ │ • Useful for foreign key relationships │ │ • Reduces round trips │ └──────────────────────────────────────────────────────────────┘
3. Use UPSERT for Synchronization ┌──────────────────────────────────────────────────────────────┐ │ • Handle race conditions gracefully │ │ • Simplify application logic │ │ • Reduce error handling complexity │ └──────────────────────────────────────────────────────────────┘
4. Use COPY for Large Data ┌──────────────────────────────────────────────────────────────┐ │ • Much faster than INSERT for bulk loading │ │ • Use for initial data loads, migrations │ │ • Consider parallel loading for very large datasets │ └──────────────────────────────────────────────────────────────┘
5. Validate Before Insert ┌──────────────────────────────────────────────────────────────┐ │ • Use CHECK constraints │ │ • Use triggers for complex validation │ │ • Consider NOT NULL constraints │ └──────────────────────────────────────────────────────────────┘ ========================================================================Next Chapter
Section titled “Next Chapter”Chapter 10: UPDATE - Modifying Data
Last Updated: February 2026