⚙️ Functions & Procedures

PostgreSQL's Swiss Army knife of built-in functions, plus how to create your own reusable SQL tools.

1️⃣ Built-in String Functions

🧒 ELI5 — What Are String Functions?

Imagine you have a box of fridge magnets — each magnet is a letter. String functions are like a set of magic tools for those magnets: one tool makes every letter UPPERCASE, another trims off blank magnets from the edges, another slices out just the first three letters. PostgreSQL ships with dozens of these built-in text-manipulation tools so you never have to do the work in your application code.

UPPER & LOWER — Change Case

SELECT UPPER('hello world') AS uppered,
       LOWER('HELLO WORLD') AS lowered;
upperedlowered
HELLO WORLDhello world

LENGTH — Count Characters

SELECT LENGTH('PostgreSQL') AS char_count;
-- Result: 10

SUBSTRING — Extract a Piece

-- SUBSTRING(string FROM start FOR length)
SELECT SUBSTRING('PostgreSQL' FROM 1 FOR 8) AS result;
-- Result: PostgreS

-- Also works with standard syntax
SELECT SUBSTRING('PostgreSQL', 8, 3) AS result;
-- Result: SQL

CONCAT & || — Join Strings Together

SELECT CONCAT('Hello', ' ', 'World') AS greeting;
-- Result: Hello World

-- PostgreSQL also supports the || operator
SELECT 'Hello' || ' ' || 'World' AS greeting;
-- Result: Hello World

⚠️ CONCAT vs || — NULL Handling Difference!

CONCAT treats NULL as an empty string: CONCAT('Hi', NULL, '!')'Hi!'. But || propagates NULL: 'Hi' || NULL || '!'NULL. Use CONCAT when your data might have NULLs!

TRIM, LTRIM, RTRIM — Remove Whitespace

SELECT TRIM('   hello   ')  AS trimmed,
       LTRIM('   hello   ') AS left_trimmed,
       RTRIM('   hello   ') AS right_trimmed;
trimmedleft_trimmedright_trimmed
'hello''hello   ''   hello'

REPLACE — Swap Text

SELECT REPLACE('Hello World', 'World', 'PostgreSQL') AS result;
-- Result: Hello PostgreSQL

SPLIT_PART — Split on a Delimiter

-- SPLIT_PART(string, delimiter, position)
SELECT SPLIT_PART('john.doe@gmail.com', '@', 1) AS username,
       SPLIT_PART('john.doe@gmail.com', '@', 2) AS domain;
usernamedomain
john.doegmail.com

LEFT, RIGHT — Grab From an End

SELECT LEFT('PostgreSQL', 4)  AS first_four,
       RIGHT('PostgreSQL', 3) AS last_three;
first_fourlast_three
PostSQL

POSITION — Find Where a Substring Starts

SELECT POSITION('SQL' IN 'PostgreSQL') AS found_at;
-- Result: 8  (1-based index)

LPAD & RPAD — Pad Strings to a Fixed Width

SELECT LPAD('42', 5, '0')  AS invoice_num,
       RPAD('Hi', 10, '.') AS dotted;
invoice_numdotted
00042Hi........

🌎 Real-Life Use Case

Imagine you run an e-commerce store and customers type their names in all sorts of crazy ways: " jOhN dOe ". You'd chain functions like this: INITCAP(TRIM(name)) to get "John Doe". String functions are essential for data cleaning.

Quick Reference: All String Functions

FunctionWhat It DoesExample → Result
UPPER(s)All uppercaseUPPER('hi') → HI
LOWER(s)All lowercaseLOWER('HI') → hi
INITCAP(s)Capitalize first lettersINITCAP('hello world') → Hello World
LENGTH(s)Character countLENGTH('abc') → 3
TRIM(s)Remove leading/trailing spacesTRIM(' hi ') → hi
REPLACE(s,a,b)Replace a with bREPLACE('abc','b','X') → aXc
SPLIT_PART(s,d,n)Split by delimiter, get nthSPLIT_PART('a-b-c','-',2) → b
LEFT(s,n)First n charsLEFT('abcde',3) → abc
RIGHT(s,n)Last n charsRIGHT('abcde',2) → de
LPAD(s,n,c)Pad from leftLPAD('5',3,'0') → 005
REVERSE(s)Reverse the stringREVERSE('abc') → cba

2️⃣ Built-in Math Functions

🧒 ELI5 — What Are Math Functions?

Remember the calculator app on your phone? It can do addition, square roots, rounding, etc. PostgreSQL has its own built-in calculator! You can do math right inside your SQL queries without pulling data into Python or Excel first. Need to round prices to 2 decimal places? Calculate a 15% tip? Find the square root of something? PostgreSQL has a function for that.

SELECT
    ROUND(3.14159, 2)   AS rounded,       -- 3.14
    CEIL(3.2)            AS ceiling,       -- 4  (round UP)
    FLOOR(3.9)           AS floored,       -- 3  (round DOWN)
    ABS(-42)             AS absolute,      -- 42
    MOD(17, 5)          AS remainder,     -- 2  (17 / 5 = 3 remainder 2)
    POWER(2, 10)        AS two_to_ten,    -- 1024
    SQRT(144)            AS square_root,   -- 12
    RANDOM()             AS random_num;    -- 0.7291... (random 0-1)

Practical Example: Calculating Sales Tax

SELECT name,
       price,
       ROUND(price * 0.18, 2) AS gst,
       ROUND(price * 1.18, 2) AS total_with_gst
FROM products
ORDER BY price DESC
LIMIT 5;

Practical Example: Random Sampling

-- Pick 3 random products for a daily deal
SELECT name, price
FROM products
ORDER BY RANDOM()
LIMIT 3;

💡 Pro Tip — ROUND vs TRUNC

ROUND(2.555, 2) gives 2.56 (rounds up). TRUNC(2.559, 2) gives 2.55 (chops off, no rounding). Use TRUNC when you need to always round down, like calculating maximum discounts.

3️⃣ Built-in Date/Time Functions

🧒 ELI5 — Why Date Functions Matter

Imagine you run a pizza delivery business. You need to answer questions like: "How many orders did we get this week?" or "How many days has it been since this customer's last order?" or "What's the busiest hour of the day?" All of those require taking dates apart, comparing them, and doing math on them. That's what date/time functions do.

Getting the Current Date & Time

SELECT
    NOW()                AS current_timestamp,  -- 2026-03-19 14:30:15.123+05:30
    CURRENT_DATE         AS today,              -- 2026-03-19
    CURRENT_TIME         AS time_now,           -- 14:30:15.123+05:30
    CURRENT_TIMESTAMP    AS same_as_now;        -- same as NOW()

EXTRACT — Pull Out Parts of a Date

SELECT
    EXTRACT(YEAR   FROM NOW()) AS year,    -- 2026
    EXTRACT(MONTH  FROM NOW()) AS month,   -- 3
    EXTRACT(DAY    FROM NOW()) AS day,     -- 19
    EXTRACT(DOW    FROM NOW()) AS weekday, -- 0=Sun, 1=Mon ... 6=Sat
    EXTRACT(HOUR   FROM NOW()) AS hour;    -- 14

DATE_TRUNC — Truncate to a Time Unit

-- Truncate to the beginning of the month
SELECT DATE_TRUNC('month', NOW()) AS month_start;
-- Result: 2026-03-01 00:00:00+05:30

-- Great for grouping orders by month
SELECT DATE_TRUNC('month', order_date) AS month,
       COUNT(*) AS total_orders
FROM orders
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month;

AGE — Difference Between Two Dates

-- How old is this order?
SELECT AGE(NOW(), '2024-06-15'::timestamp) AS order_age;
-- Result: 1 year 9 mons 4 days 14:30:15.123

-- How long has each employee been with us?
SELECT name,
       hire_date,
       AGE(NOW(), hire_date) AS tenure
FROM employees
ORDER BY hire_date;

TO_CHAR — Format Dates as Strings

SELECT
    TO_CHAR(NOW(), 'DD-Mon-YYYY')          AS formatted1,  -- 19-Mar-2026
    TO_CHAR(NOW(), 'Day, DD Month YYYY')  AS formatted2,  -- Thursday , 19 March 2026
    TO_CHAR(NOW(), 'HH12:MI AM')           AS formatted3;  -- 02:30 PM

Interval Arithmetic — Adding/Subtracting Time

SELECT
    NOW() + INTERVAL '7 days'    AS next_week,
    NOW() - INTERVAL '3 months'  AS three_months_ago,
    NOW() + INTERVAL '2 hours'   AS in_two_hours;

-- Practical: Find orders from the last 30 days
SELECT * FROM orders
WHERE order_date >= NOW() - INTERVAL '30 days';

🌎 Real-Life Use Case

Subscription services use date functions constantly: "When does this user's trial expire?" → signup_date + INTERVAL '14 days'. "Has the subscription lapsed?" → WHERE expiry_date < NOW(). "Show monthly revenue trends" → GROUP BY DATE_TRUNC('month', payment_date).

4️⃣ Conditional Functions

🧒 ELI5 — Smart Defaults and Choices

Imagine you’re ordering food online. If your favourite dish is available, you order that. If not, you pick your backup. If that’s gone too, you just get pizza. Conditional functions work the same way — they make smart choices based on the data they see.

COALESCE — First Non-NULL Value

SELECT name,
       COALESCE(phone, email, 'No contact info') AS contact
FROM customers;

NULLIF — Return NULL If Values Match

-- Avoid division by zero
SELECT total_revenue / NULLIF(total_orders, 0) AS avg_order_value
FROM monthly_stats;

GREATEST & LEAST — Find Max/Min Among Values

SELECT
    GREATEST(10, 25, 5, 30) AS biggest,    -- 30
    LEAST(10, 25, 5, 30)    AS smallest;   -- 5

-- Practical: Ensure price is never below the minimum
SELECT name,
       GREATEST(price, 9.99) AS final_price
FROM products;

CASE (Recap) — SQL's If-Then-Else

SELECT name, salary,
       CASE
           WHEN salary >= 90000 THEN 'Senior'
           WHEN salary >= 70000 THEN 'Mid-Level'
           ELSE 'Junior'
       END AS level
FROM employees;

5️⃣ CREATE FUNCTION — Your Own Reusable Tools

🧒 ELI5 — What Is a User-Defined Function?

Think of a function like a recipe card. Instead of remembering all the steps to bake chocolate chip cookies every single time, you write them on a card once. From now on, you just say "follow the cookie recipe" and you get cookies! A PostgreSQL function is the same — you write the SQL logic once, give it a name, and then call it whenever you want. Less repetition, fewer mistakes.

SQL Language Functions — The Simple Kind

-- A function to calculate 18% GST on any price
CREATE FUNCTION calculate_gst(price NUMERIC)
RETURNS NUMERIC
LANGUAGE sql
AS $$
    SELECT ROUND(price * 0.18, 2);
$$;

⚠️ What Is $$ Dollar-Quoting?

In PostgreSQL, the body of a function must be a string. Instead of wrapping it in single quotes (which gets messy when your SQL itself uses single quotes), PostgreSQL lets you use $$ as the string delimiter. Everything between $$ and $$ is the function body. Think of $$ as "super quotes."

Using the Function

SELECT name,
       price,
       calculate_gst(price) AS gst,
       price + calculate_gst(price) AS total
FROM products;

Function to Format Currency

CREATE FUNCTION format_inr(amount NUMERIC)
RETURNS TEXT
LANGUAGE sql
AS $$
    SELECT '₹' || TO_CHAR(amount, 'FM99,99,999.00');
$$;

-- Usage
SELECT format_inr(1250000.50);
-- Result: ₹12,50,000.50

Function to Validate Email (Basic)

CREATE FUNCTION is_valid_email(email TEXT)
RETURNS BOOLEAN
LANGUAGE sql
AS $$
    SELECT email ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
$$;

-- Usage
SELECT is_valid_email('john@example.com');  -- true
SELECT is_valid_email('not-an-email');      -- false

💡 Pro Tip — LANGUAGE sql vs LANGUAGE plpgsql

LANGUAGE sql functions are simple: they contain one or more SQL statements and return the result of the last one. They're fast because PostgreSQL can inline them. Use LANGUAGE plpgsql when you need variables, IF/ELSE logic, loops, or error handling. We'll cover plpgsql next!

6️⃣ PL/pgSQL Basics — Real Programming Inside PostgreSQL

🧒 ELI5 — What Is PL/pgSQL?

Regular SQL is like a vending machine — you press a button and get one thing. PL/pgSQL is like a robot chef — it can check ingredients (IF/THEN), repeat steps (LOOP), adjust the recipe based on what it finds, and even shout a warning if something goes wrong (RAISE NOTICE). It's a full programming language that lives inside PostgreSQL.

Structure of a PL/pgSQL Function

CREATE FUNCTION function_name(params)
RETURNS return_type
LANGUAGE plpgsql
AS $$
DECLARE
    -- Variables go here
    my_variable INTEGER;
BEGIN
    -- Logic goes here
    RETURN result;
END;
$$;

Variables

CREATE FUNCTION greet(person_name TEXT)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
    greeting TEXT;
BEGIN
    greeting := 'Hello, ' || person_name || '! Welcome to PostgreSQL.';
    RETURN greeting;
END;
$$;

SELECT greet('Alice');
-- Result: Hello, Alice! Welcome to PostgreSQL.

IF / THEN / ELSE

CREATE FUNCTION classify_customer(total_spending NUMERIC)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
    tier TEXT;
BEGIN
    IF total_spending >= 100000 THEN
        tier := '💎 Platinum';
    ELSIF total_spending >= 50000 THEN
        tier := '🥇 Gold';
    ELSIF total_spending >= 10000 THEN
        tier := '🥈 Silver';
    ELSE
        tier := '🥉 Bronze';
    END IF;

    RETURN tier;
END;
$$;

-- Usage: Classify all customers
SELECT name,
       total_spending,
       classify_customer(total_spending) AS tier
FROM customers
ORDER BY total_spending DESC;

LOOP & RAISE NOTICE

CREATE FUNCTION countdown(start_num INTEGER)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
    i INTEGER := start_num;
BEGIN
    WHILE i > 0 LOOP
        RAISE NOTICE '% ...', i;
        i := i - 1;
    END LOOP;
    RAISE NOTICE 'Liftoff! 🚀';
END;
$$;

SELECT countdown(3);
-- NOTICE:  3 ...
-- NOTICE:  2 ...
-- NOTICE:  1 ...
-- NOTICE:  Liftoff! 🚀

🌎 Real-Life Analogy

RAISE NOTICE is like console.log() in JavaScript or print() in Python. It's your debugging friend — it outputs messages to the PostgreSQL log so you can see what your function is doing step by step. There's also RAISE WARNING and RAISE EXCEPTION (which stops the function with an error).

FOR Loop — Loop Through Query Results

CREATE FUNCTION list_expensive_products(min_price NUMERIC)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
    product RECORD;
    result TEXT := '';
BEGIN
    FOR product IN
        SELECT name, price FROM products
        WHERE price >= min_price
        ORDER BY price DESC
    LOOP
        result := result || product.name || ' ($' || product.price || '), ';
    END LOOP;

    RETURN RTRIM(result, ', ');
END;
$$;

7️⃣ Stored Procedures

🧒 ELI5 — Functions vs Procedures

A function is like a blender — you put ingredients in, you get a smoothie out (a return value). A procedure is like a dishwasher — you press start and it does a bunch of work (washing, rinsing, drying) but it doesn't "return" anything to your hand. The key difference: procedures can commit and rollback transactions in the middle of their work. Functions cannot.

CREATE PROCEDURE

CREATE PROCEDURE transfer_funds(
    sender_id INTEGER,
    receiver_id INTEGER,
    amount NUMERIC
)
LANGUAGE plpgsql
AS $$
BEGIN
    -- Deduct from sender
    UPDATE accounts
    SET balance = balance - amount
    WHERE id = sender_id;

    -- Add to receiver
    UPDATE accounts
    SET balance = balance + amount
    WHERE id = receiver_id;

    COMMIT;
END;
$$;

CALL — How to Run a Procedure

-- Transfer $500 from account 1 to account 2
CALL transfer_funds(1, 2, 500.00);

Functions vs Procedures — Quick Comparison

FeatureFunctionProcedure
Returns a value?Yes (always)No (use OUT params)
Can be used in SELECT?YesNo
Can COMMIT/ROLLBACK?NoYes
How to call?SELECT func()CALL proc()
Introduced in PGAlways existedPostgreSQL 11+

💡 Pro Tip — When to Use Procedures

Use procedures for multi-step operations that need transaction control: bank transfers, batch updates, end-of-day processing, data migration scripts. Use functions for calculations, transformations, and anything you want to call inside a SELECT statement.

8️⃣ Triggers — Automatic Actions

🧒 ELI5 — What Is a Trigger?

Imagine a doorbell. Nobody has to stand outside and watch for visitors — when someone presses the button (an event), the bell rings automatically (an action). A trigger is a database doorbell: when something happens to a table (INSERT, UPDATE, DELETE), PostgreSQL automatically runs a function you defined. No human intervention needed.

When Triggers Fire

TimingMeaningUse Case
BEFORERuns before the change is savedValidate/modify data before it's written
AFTERRuns after the change is savedLog the change, send notifications
INSTEAD OFReplaces the action (views only)Make views updatable

Special Variables in Triggers

VariableWhat It Holds
NEWThe new row being inserted or updated
OLDThe old row before update or deletion
TG_OPThe operation: 'INSERT', 'UPDATE', or 'DELETE'
TG_TABLE_NAMEName of the table that fired the trigger

Example: Auto-Update "updated_at" Timestamp

-- Step 1: Create the trigger function
CREATE FUNCTION set_updated_at()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
    NEW.updated_at := NOW();
    RETURN NEW;
END;
$$;

-- Step 2: Attach the trigger to a table
CREATE TRIGGER trg_employees_updated_at
    BEFORE UPDATE ON employees
    FOR EACH ROW
    EXECUTE FUNCTION set_updated_at();

Now, every time you UPDATE any employee row, the updated_at column is automatically set to the current time. You never have to remember to do it manually!

Example: Audit Log Trigger

-- First, create an audit log table
CREATE TABLE audit_log (
    id          SERIAL PRIMARY KEY,
    table_name  TEXT,
    operation   TEXT,
    old_data    JSONB,
    new_data    JSONB,
    changed_at  TIMESTAMP DEFAULT NOW(),
    changed_by  TEXT DEFAULT CURRENT_USER
);

-- Create the audit trigger function
CREATE FUNCTION log_changes()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
    IF TG_OP = 'DELETE' THEN
        INSERT INTO audit_log (table_name, operation, old_data)
        VALUES (TG_TABLE_NAME, 'DELETE', to_jsonb(OLD));
        RETURN OLD;
    ELSIF TG_OP = 'UPDATE' THEN
        INSERT INTO audit_log (table_name, operation, old_data, new_data)
        VALUES (TG_TABLE_NAME, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW));
        RETURN NEW;
    ELSIF TG_OP = 'INSERT' THEN
        INSERT INTO audit_log (table_name, operation, new_data)
        VALUES (TG_TABLE_NAME, 'INSERT', to_jsonb(NEW));
        RETURN NEW;
    END IF;
END;
$$;

-- Attach it to the employees table
CREATE TRIGGER trg_employees_audit
    AFTER INSERT OR UPDATE OR DELETE ON employees
    FOR EACH ROW
    EXECUTE FUNCTION log_changes();

🌎 Real-Life Use Case

Audit triggers are used by banks, hospitals, and any regulated industry. If a nurse changes a patient's medication record, the audit log captures exactly what changed, when, and by whom. This is often a legal requirement. PostgreSQL triggers make this automatic and tamper-proof.

⚠️ Trigger Performance Warning

Triggers run for every single row affected by the operation. If you UPDATE 1 million rows, the trigger function executes 1 million times. Keep trigger functions fast and lightweight. For heavy work, consider using AFTER triggers with deferred processing or a message queue.

9️⃣ Views — Virtual Tables

🧒 ELI5 — What Is a View?

Imagine you look out your bedroom window. You see a beautiful garden, but you don't own the garden — it belongs to the park next door. The window gives you a view of something that exists elsewhere. A database view is the same: it's a saved query that looks and behaves like a table, but it doesn't store any data itself. Every time you query the view, it runs the underlying query and shows you fresh results.

CREATE VIEW

-- A view that shows only Engineering employees
CREATE VIEW engineering_team AS
SELECT id, name, salary, hire_date
FROM employees
WHERE department = 'Engineering';

-- Now query it like a regular table!
SELECT * FROM engineering_team;

Simplifying Complex Queries

-- A view that joins orders with customers and calculates totals
CREATE VIEW order_summary AS
SELECT
    c.name AS customer_name,
    COUNT(o.id) AS total_orders,
    SUM(o.amount) AS total_spent,
    MAX(o.order_date) AS last_order
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.name;

-- Now anyone can run this simple query instead of the complex one
SELECT * FROM order_summary
WHERE total_spent > 10000;

Updatable Views

Simple views (no JOINs, no GROUP BY, no DISTINCT) are automatically updatable:

-- You can INSERT/UPDATE/DELETE through the view!
UPDATE engineering_team
SET salary = 95000
WHERE name = 'Alice Johnson';
-- This actually updates the employees table

WITH CHECK OPTION — Prevent Sneaky Inserts

CREATE VIEW engineering_team AS
SELECT id, name, department, salary
FROM employees
WHERE department = 'Engineering'
WITH CHECK OPTION;

-- This will FAIL because 'Sales' doesn't match the view's WHERE
INSERT INTO engineering_team (name, department, salary)
VALUES ('Sneaky Pete', 'Sales', 50000);
-- ERROR: new row violates check option for view "engineering_team"

💡 Pro Tip — Views for Security

Views are great for access control. You can give a junior analyst access to a view that only shows non-sensitive columns (no salary, no SSN) while the underlying table contains everything. The analyst doesn't even need to know the full table exists!

CREATE OR REPLACE VIEW

-- Modify an existing view without dropping it first
CREATE OR REPLACE VIEW engineering_team AS
SELECT id, name, salary, hire_date,
       AGE(NOW(), hire_date) AS tenure
FROM employees
WHERE department = 'Engineering';

Dropping a View

DROP VIEW IF EXISTS engineering_team;

🏋️ Practice Exercises

Exercise 1: String Manipulation

Write a query that takes the email 'john.doe@company.com' and extracts just the username (before the @), converts it to title case, and replaces the dot with a space. Expected result: John Doe.

Reveal Solution
SELECT INITCAP(
    REPLACE(
        SPLIT_PART('john.doe@company.com', '@', 1),
        '.',
        ' '
    )
) AS full_name;

Exercise 2: Create a Tax Calculator Function

Create a function calc_total(price NUMERIC, tax_rate NUMERIC DEFAULT 0.18) that returns the price including tax, rounded to 2 decimal places.

Reveal Solution
CREATE FUNCTION calc_total(price NUMERIC, tax_rate NUMERIC DEFAULT 0.18)
RETURNS NUMERIC
LANGUAGE sql
AS $$
    SELECT ROUND(price * (1 + tax_rate), 2);
$$;

Exercise 3: Build an Audit Trigger

Create a trigger on the products table that automatically sets updated_at to NOW() whenever a product's price changes.

Reveal Solution
CREATE FUNCTION update_product_timestamp()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
    IF OLD.price <> NEW.price THEN
        NEW.updated_at := NOW();
    END IF;
    RETURN NEW;
END;
$$;

CREATE TRIGGER trg_product_price_change
    BEFORE UPDATE ON products
    FOR EACH ROW
    EXECUTE FUNCTION update_product_timestamp();

📝 Quiz — Test Your Knowledge!

Question 1: What does SPLIT_PART('a-b-c', '-', 2) return?

Question 2: What is the main difference between a FUNCTION and a PROCEDURE?

Question 3: When does a BEFORE UPDATE trigger fire?

Question 4: What does $$ (dollar-quoting) do in a function definition?

Question 5: What is a VIEW?

Question 6: What does the trigger variable NEW represent?

Question 7: What does WITH CHECK OPTION do on a view?

🎉 What's Next?

12+
String functions learned
8
Math functions mastered
6
Date/time tools explored
4
Trigger & view patterns built

You’ve leveled up from a SQL user to a SQL programmer. Here’s what you accomplished:

TopicKey Takeaway
String FunctionsUPPER, LOWER, TRIM, REPLACE, SPLIT_PART, LPAD — clean and transform text
Math FunctionsROUND, CEIL, FLOOR, POWER, RANDOM — do calculations in the database
Date/Time FunctionsNOW, EXTRACT, DATE_TRUNC, AGE, intervals — master time-based queries
Conditional FunctionsCOALESCE, NULLIF, GREATEST, LEAST, CASE — smart defaults and choices
User-Defined FunctionsCREATE FUNCTION with SQL and PL/pgSQL — reusable logic
ProceduresCREATE PROCEDURE + CALL — multi-step operations with transaction control
TriggersBEFORE/AFTER triggers — automatic auditing and timestamp updates
ViewsVirtual tables that simplify complex queries and enhance security

🔮 Coming Up Next: Transactions & Concurrency

What happens when two people try to buy the last item in stock at the exact same time? How does a bank ensure money doesn’t vanish during a transfer? In the next module, you’ll learn about transactions, ACID properties, isolation levels, and MVCC — the technology that keeps your data safe when thousands of users hit the database at once. It’s one of the most important topics in all of database engineering!

Indexes & Performance Transactions & Concurrency