Tutorial 01: Basic CRUD Operations
Tutorial 01: Basic CRUD Operations
Time: 15 minutes Level: Basic Prerequisites: 5-minute Quickstart completed
What You’ll Learn
By the end of this tutorial, you’ll be able to:
- Create tables with various data types
- Insert, update, and delete data
- Query data with SELECT statements
- Use transactions for data consistency
- Handle errors and rollbacks
Real-World Use Case
You’re building an e-commerce application and need to manage a product catalog. You’ll learn how to:
- Store product information (name, price, inventory)
- Update prices and stock levels
- Delete discontinued products
- Query products by category and price range
- Ensure data consistency with transactions
Setup (2 minutes)
Start HeliosDB
If you haven’t already, start HeliosDB:
docker run -d \ --name heliosdb \ -p 5432:5432 \ -e HELIOS_PASSWORD=mysecretpassword \ heliosdb/heliosdb:latestConnect to HeliosDB
psql -h localhost -p 5432 -U helios -d helios# Password: mysecretpasswordAlternative: Use any PostgreSQL client (DBeaver, pgAdmin, TablePlus, etc.)
Step 1: Create a Table (3 minutes)
Goal
Create a products table to store e-commerce product data.
Code
CREATE TABLE products ( product_id SERIAL PRIMARY KEY, name VARCHAR(200) NOT NULL, description TEXT, price DECIMAL(10, 2) NOT NULL CHECK (price >= 0), stock_quantity INTEGER DEFAULT 0 CHECK (stock_quantity >= 0), category VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);Execute
Copy-paste the SQL above into your psql client and press Enter.
Expected Output
CREATE TABLEWhat Just Happened?
Let’s break down the table definition:
product_id SERIAL PRIMARY KEY: Auto-incrementing ID (1, 2, 3, …)name VARCHAR(200) NOT NULL: Product name, max 200 chars, requiredprice DECIMAL(10, 2) NOT NULL CHECK (price >= 0): Price with 2 decimal places, must be non-negativestock_quantity INTEGER DEFAULT 0: Inventory count, defaults to 0CHECKconstraints: Ensure data integrity (no negative prices/stock)created_at,updated_at: Automatic timestamps
HeliosDB Optimization: The table is automatically optimized with:
- Primary key B-tree index on
product_id - Autonomous indexing will suggest indexes on
categoryafter queries
Verify
Check that the table was created:
\dt productsOutput:
List of relations Schema | Name | Type | Owner--------+----------+-------+------- public | products | table | heliosStep 2: Insert Data (3 minutes)
Goal
Add products to the catalog.
Single Insert
INSERT INTO products (name, description, price, stock_quantity, category)VALUES ( 'Wireless Mouse', 'Ergonomic wireless mouse with 3-button design', 24.99, 150, 'Electronics');Expected Output
INSERT 0 1This means 1 row was inserted (product_id was auto-generated).
Multiple Inserts (Batch)
INSERT INTO products (name, description, price, stock_quantity, category) VALUES ('Mechanical Keyboard', 'RGB backlit mechanical keyboard with Cherry MX switches', 89.99, 75, 'Electronics'), ('USB-C Cable', '6ft USB-C to USB-C charging cable', 12.99, 500, 'Accessories'), ('Laptop Stand', 'Adjustable aluminum laptop stand', 39.99, 200, 'Accessories'), ('Webcam HD', '1080p HD webcam with built-in microphone', 59.99, 120, 'Electronics'), ('Desk Lamp', 'LED desk lamp with adjustable brightness', 34.99, 80, 'Office'), ('Notebook', 'Hardcover notebook, 200 pages', 8.99, 300, 'Office'), ('Wireless Headphones', 'Noise-canceling over-ear headphones', 149.99, 60, 'Electronics'), ('Monitor', '27-inch 4K UHD monitor', 349.99, 45, 'Electronics'), ('Phone Case', 'Protective phone case for iPhone 15', 19.99, 250, 'Accessories');Expected Output
INSERT 0 99 rows inserted successfully!
Insert with Returned Values
HeliosDB supports PostgreSQL’s RETURNING clause:
INSERT INTO products (name, price, category)VALUES ('Gaming Mouse', 49.99, 'Electronics')RETURNING product_id, name, created_at;Expected Output
product_id | name | created_at------------+--------------+---------------------------- 11 | Gaming Mouse | 2025-11-07 10:45:23.123456Why this matters: Get the auto-generated ID immediately without a separate SELECT.
Step 3: Query Data (SELECT) (3 minutes)
Goal
Retrieve products using various query techniques.
Basic SELECT - All Products
SELECT * FROM products;Output: All 11 products with all columns.
Select Specific Columns
SELECT product_id, name, price, stock_quantityFROM products;Best Practice: Only select columns you need (faster queries, less network traffic).
Filter with WHERE
-- Products in Electronics categorySELECT name, price, stock_quantityFROM productsWHERE category = 'Electronics';Output: 5 electronics products.
Filter by Price Range
-- Products under $50SELECT name, price, categoryFROM productsWHERE price < 50ORDER BY price ASC;Output: Products sorted by price (cheapest first).
Pattern Matching with LIKE
-- Products with "Mouse" in the nameSELECT name, priceFROM productsWHERE name LIKE '%Mouse%';Output:
name | price--------------------+------- Wireless Mouse | 24.99 Gaming Mouse | 49.99Wildcards:
%= any characters (0 or more)_= single character
Combining Conditions
-- Electronics products under $100 with stock > 50SELECT name, price, stock_quantityFROM productsWHERE category = 'Electronics' AND price < 100 AND stock_quantity > 50;Operators:
AND- both conditions must be trueOR- at least one condition must be trueNOT- negates a condition
Aggregations
-- Total inventory value by categorySELECT category, COUNT(*) AS product_count, SUM(stock_quantity) AS total_stock, AVG(price) AS avg_price, SUM(price * stock_quantity) AS total_valueFROM productsGROUP BY categoryORDER BY total_value DESC;Expected Output:
category | product_count | total_stock | avg_price | total_value--------------+---------------+-------------+----------------------+------------------ Electronics | 6 | 450 | 82.489999999999995 | 37122.50 Accessories | 3 | 950 | 24.323333333333334 | 23108.50 Office | 2 | 380 | 21.99 | 8356.20Aggregate Functions:
COUNT(*)- number of rowsSUM(column)- totalAVG(column)- averageMIN(column)- minimum valueMAX(column)- maximum value
Step 4: Update Data (2 minutes)
Goal
Modify existing product data.
Update a Single Product
-- Increase price of Wireless Mouse by 10%UPDATE productsSET price = price * 1.10, updated_at = CURRENT_TIMESTAMPWHERE name = 'Wireless Mouse';Expected Output
UPDATE 11 row was updated.
Verify the Update
SELECT name, price, updated_atFROM productsWHERE name = 'Wireless Mouse';Output:
name | price | updated_at----------------+-------+---------------------------- Wireless Mouse | 27.49 | 2025-11-07 10:50:15.654321Price increased from $24.99 to $27.49 (10% increase).
Update Multiple Rows
-- Restock all products in Office categoryUPDATE productsSET stock_quantity = stock_quantity + 100WHERE category = 'Office';Output: UPDATE 2 (2 office products updated).
Update with RETURNING
-- Reduce stock after a saleUPDATE productsSET stock_quantity = stock_quantity - 5WHERE product_id = 1RETURNING name, stock_quantity AS new_stock;Output:
name | new_stock----------------+----------- Wireless Mouse | 145Step 5: Delete Data (2 minutes)
Goal
Remove products from the catalog.
Delete a Single Product
-- Delete discontinued productDELETE FROM productsWHERE name = 'Notebook';Expected Output
DELETE 1Delete with Condition
-- Remove out-of-stock productsDELETE FROM productsWHERE stock_quantity = 0;Safety Note: Always use WHERE clause! Without it, ALL rows are deleted.
Delete with RETURNING
-- Remove products under $10 and log what was deletedDELETE FROM productsWHERE price < 10RETURNING product_id, name, price;Output: Shows what was deleted.
Step 6: Transactions (3 minutes)
Goal
Ensure data consistency with ACID transactions.
Scenario: Process an Order
When a customer orders 10 Wireless Mice:
- Reduce stock by 10
- If stock goes below 0, rollback (prevent overselling)
Transaction with COMMIT
BEGIN;
-- Check current stockSELECT stock_quantity FROM products WHERE product_id = 1;
-- Reduce stockUPDATE productsSET stock_quantity = stock_quantity - 10WHERE product_id = 1;
-- VerifySELECT stock_quantity FROM products WHERE product_id = 1;
COMMIT;What Happened:
BEGINstarts a transaction- All changes are temporary until
COMMIT COMMITmakes changes permanent
Transaction with ROLLBACK
BEGIN;
-- Try to reduce stock by 200 (more than available)UPDATE productsSET stock_quantity = stock_quantity - 200WHERE product_id = 1;
-- Check stock (would be negative!)SELECT stock_quantity FROM products WHERE product_id = 1;
-- Abort the transactionROLLBACK;
-- Verify stock unchangedSELECT stock_quantity FROM products WHERE product_id = 1;What Happened:
ROLLBACKundoes all changes sinceBEGIN- Data integrity preserved
Transaction with Constraint Enforcement
BEGIN;
-- This will fail due to CHECK constraint (stock_quantity >= 0)UPDATE productsSET stock_quantity = -50WHERE product_id = 1;-- ERROR: new row for relation "products" violates check constraintAutomatic Rollback: HeliosDB automatically rolls back on errors.
Complete Example: Order Processing System
Here’s a complete transaction that processes a customer order:
-- Create orders tableCREATE TABLE orders ( order_id SERIAL PRIMARY KEY, product_id INTEGER REFERENCES products(product_id), quantity INTEGER NOT NULL CHECK (quantity > 0), order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
-- Process an order (atomic operation)BEGIN;
-- Check product availabilityDO $$DECLARE available_stock INTEGER;BEGIN SELECT stock_quantity INTO available_stock FROM products WHERE product_id = 1;
IF available_stock >= 10 THEN -- Sufficient stock, process order INSERT INTO orders (product_id, quantity) VALUES (1, 10);
UPDATE products SET stock_quantity = stock_quantity - 10 WHERE product_id = 1;
RAISE NOTICE 'Order processed successfully'; ELSE RAISE EXCEPTION 'Insufficient stock: % available', available_stock; END IF;END $$;
COMMIT;Production-Ready: This pattern ensures atomic order processing.
Troubleshooting
Problem: “relation ‘products’ does not exist”
Cause: Table hasn’t been created yet.
Solution:
-- Check if table exists\dt products
-- If not, create it (see Step 1)Problem: “ERROR: duplicate key value violates unique constraint”
Cause: Trying to insert duplicate primary key or unique value.
Solution:
-- Let SERIAL auto-generate IDs, don't specify product_idINSERT INTO products (name, price) VALUES ('New Product', 29.99);Problem: “ERROR: check constraint violated”
Cause: Data violates CHECK constraint (e.g., negative price).
Solution:
-- Ensure values meet constraintsINSERT INTO products (name, price, stock_quantity)VALUES ('Valid Product', 19.99, 50); -- Both price and stock are >= 0Problem: Updates affect more rows than expected
Cause: WHERE clause too broad.
Solution:
-- Always verify with SELECT firstSELECT * FROM products WHERE category = 'Electronics';
-- Then updateUPDATE products SET price = price * 1.1 WHERE category = 'Electronics';Performance Tips
1. Use Indexes for Frequent Queries
-- If you often query by categoryCREATE INDEX idx_products_category ON products(category);
-- HeliosDB autonomous indexing will suggest this automatically!2. Batch Inserts
-- SLOW: One row at a time (avoid)INSERT INTO products (name, price) VALUES ('Product 1', 10);INSERT INTO products (name, price) VALUES ('Product 2', 20);
-- FAST: Batch insertINSERT INTO products (name, price) VALUES ('Product 1', 10), ('Product 2', 20), ('Product 3', 30);Performance: Batch inserts are 10-100x faster!
3. Use EXPLAIN to Analyze Queries
EXPLAIN ANALYZESELECT * FROM products WHERE category = 'Electronics';Output: Shows query execution plan, index usage, timing.
Next Steps
Congratulations! You’ve mastered basic CRUD operations in HeliosDB.
Continue Learning
- Tutorial 02: Advanced Queries - Joins, CTEs, window functions
- Tutorial 03: Multi-Protocol - Connect via Oracle, MySQL
- Tutorial 09: Indexing & Optimization - Performance tuning
Real-World Applications
- E-commerce: Product catalogs, inventory management
- SaaS: User management, subscription tracking
- Analytics: Data warehousing, reporting
- IoT: Time-series data, sensor readings
Get Help
- Stuck? Check the Troubleshooting Guide
- Questions? See the FAQ
- Community: Join Discord
Complete Code Reference
Here’s the complete SQL from this tutorial:
-- Create tableCREATE TABLE products ( product_id SERIAL PRIMARY KEY, name VARCHAR(200) NOT NULL, description TEXT, price DECIMAL(10, 2) NOT NULL CHECK (price >= 0), stock_quantity INTEGER DEFAULT 0 CHECK (stock_quantity >= 0), category VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
-- Insert dataINSERT INTO products (name, description, price, stock_quantity, category) VALUES ('Wireless Mouse', 'Ergonomic wireless mouse', 24.99, 150, 'Electronics'), ('Mechanical Keyboard', 'RGB backlit keyboard', 89.99, 75, 'Electronics'), ('USB-C Cable', '6ft charging cable', 12.99, 500, 'Accessories'), ('Laptop Stand', 'Adjustable aluminum stand', 39.99, 200, 'Accessories'), ('Webcam HD', '1080p webcam', 59.99, 120, 'Electronics');
-- Query dataSELECT * FROM products WHERE category = 'Electronics';
-- Update dataUPDATE products SET price = price * 1.10 WHERE product_id = 1;
-- Delete dataDELETE FROM products WHERE stock_quantity = 0;
-- TransactionBEGIN;UPDATE products SET stock_quantity = stock_quantity - 10 WHERE product_id = 1;COMMIT;Run this code: All examples are tested and ready to copy-paste!
Tutorial Complete! 🎉
You’ve learned the fundamentals of database operations. Ready for more? Start Tutorial 02: Advanced Queries.