PostgreSQL reference
PostgreSQL cheat sheet
Copy working examples for SELECT, filters, joins, inserts, updates, CTEs, window functions, JSONB, arrays, indexes, and routine database administration. The examples use PostgreSQL syntax and common sample tables such as users, orders, and products.
SELECT
Retrieve data from tables
Select All Columns
Retrieve all columns from a table
SELECT * FROM users;Select Specific Columns
Retrieve only the columns you need
SELECT name, email, created_atFROM users;Select Distinct Values
Remove duplicate rows from results
SELECT DISTINCT countryFROM users;Column Aliases
Rename columns in output using AS
SELECT first_name AS "First Name", last_name AS "Last Name", email AS "Email Address"FROM users;Concatenate Columns
Combine multiple columns into one
SELECT first_name || ' ' || last_name AS full_name, emailFROM users;CASE Expression
Conditional logic in SELECT
SELECT name, price, CASE WHEN price < 10 THEN 'Budget' WHEN price < 50 THEN 'Standard' ELSE 'Premium' END AS price_tierFROM products;Handle NULL Values
Return first non-null value
SELECT name, COALESCE(nickname, name) AS display_name, COALESCE(phone, 'No phone') AS phoneFROM users;NULLIF Function
Return NULL if two values are equal
SELECT name, NULLIF(status, 'inactive') AS active_statusFROM users;WHERE
Filter rows with conditions
Equal Comparison
Filter rows matching exact value
SELECT * FROM usersWHERE status = 'active';Comparison Operators
Use >, <, >=, <=, <> for comparisons
SELECT * FROM productsWHERE price >= 100 AND price <= 500;BETWEEN Range
Filter values within a range (inclusive)
SELECT * FROM ordersWHERE created_at BETWEEN '2024-01-01' AND '2024-12-31';IN List
Match any value in a list
SELECT * FROM usersWHERE country IN ('USA', 'Canada', 'UK');LIKE Pattern Matching
Match patterns with wildcards
-- % matches any sequenceSELECT * FROM users WHERE email LIKE '%@gmail.com'; -- _ matches single characterSELECT * FROM products WHERE sku LIKE 'ABC-___';ILIKE (Case Insensitive)
Pattern matching ignoring case
SELECT * FROM usersWHERE name ILIKE '%john%';NULL Checks
Check for NULL or NOT NULL
-- Find rows with NULLSELECT * FROM users WHERE phone IS NULL; -- Find rows without NULLSELECT * FROM users WHERE phone IS NOT NULL;AND / OR Logic
Combine multiple conditions
SELECT * FROM usersWHERE (status = 'active' OR status = 'pending') AND created_at > '2024-01-01';NOT Operator
Negate conditions
SELECT * FROM usersWHERE NOT (status = 'deleted' OR status = 'banned');EXISTS Subquery
Check if subquery returns rows
SELECT * FROM users uWHERE EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id);ANY / SOME Operator
Compare against any subquery value
-- Equal to any valueSELECT * FROM productsWHERE price = ANY(SELECT price FROM featured_products); -- Greater than any (same as > MIN)SELECT * FROM productsWHERE price > ANY(SELECT price FROM budget_products);ALL Operator
Compare against all subquery values
-- Greater than all valuesSELECT * FROM productsWHERE price > ALL(SELECT price FROM budget_products); -- Not equal to all (exclude all matches)SELECT * FROM usersWHERE id <> ALL(SELECT user_id FROM banned_users);ORDER & LIMIT
Sort and paginate results
Sort Ascending
Sort results A-Z, smallest to largest
SELECT * FROM usersORDER BY created_at ASC;Sort Descending
Sort results Z-A, largest to smallest
SELECT * FROM productsORDER BY price DESC;Multi-Column Sort
Sort by multiple columns
SELECT * FROM usersORDER BY country ASC, created_at DESC;NULL Ordering
Control where NULLs appear
SELECT * FROM usersORDER BY phone NULLS LAST; -- Or put NULLs firstSELECT * FROM usersORDER BY phone NULLS FIRST;LIMIT Results
Return only first N rows
SELECT * FROM productsORDER BY created_at DESCLIMIT 10;OFFSET for Pagination
Skip rows for pagination
-- Page 1 (rows 1-10)SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 0; -- Page 2 (rows 11-20)SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 10; -- Page 3 (rows 21-30)SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 20;FETCH FIRST (SQL Standard)
Standard SQL alternative to LIMIT
SELECT * FROM productsORDER BY price DESCFETCH FIRST 5 ROWS ONLY;Aggregates
COUNT, SUM, AVG, MIN, MAX
COUNT All Rows
Count total number of rows
SELECT COUNT(*) AS total_usersFROM users;COUNT Distinct
Count unique values
SELECT COUNT(DISTINCT country) AS countriesFROM users;SUM Values
Calculate total of numeric column
SELECT SUM(amount) AS total_revenueFROM ordersWHERE status = 'completed';AVG Average
Calculate average of numeric column
SELECT AVG(price) AS average_priceFROM products;MIN and MAX
Find smallest and largest values
SELECT MIN(price) AS cheapest, MAX(price) AS most_expensiveFROM products;Aggregate with FILTER
Apply conditions to aggregates
SELECT COUNT(*) AS total_orders, COUNT(*) FILTER (WHERE status = 'completed') AS completed, COUNT(*) FILTER (WHERE status = 'pending') AS pendingFROM orders;GROUP BY
Group rows and aggregate
Basic Grouping
Group rows by column value
SELECT country, COUNT(*) AS user_countFROM usersGROUP BY country;Group by Multiple Columns
Create groups from column combinations
SELECT country, status, COUNT(*) AS user_countFROM usersGROUP BY country, status;HAVING Filter
Filter groups after aggregation
SELECT country, COUNT(*) AS user_countFROM usersGROUP BY countryHAVING COUNT(*) > 100;ROLLUP Subtotals
Generate subtotals and grand total
SELECT country, city, COUNT(*) AS user_countFROM usersGROUP BY ROLLUP(country, city);CUBE All Combinations
Generate all grouping combinations
SELECT country, status, COUNT(*) AS user_countFROM usersGROUP BY CUBE(country, status);JOINs
Combine data from multiple tables
INNER JOIN
Return matching rows from both tables
SELECT users.name, orders.id AS order_id, orders.totalFROM usersINNER JOIN orders ON users.id = orders.user_id;LEFT JOIN
All rows from left, matching from right
SELECT users.name, orders.id AS order_idFROM usersLEFT JOIN orders ON users.id = orders.user_id;RIGHT JOIN
Matching from left, all from right
SELECT users.name, orders.id AS order_idFROM usersRIGHT JOIN orders ON users.id = orders.user_id;FULL OUTER JOIN
All rows from both tables
SELECT users.name, orders.id AS order_idFROM usersFULL OUTER JOIN orders ON users.id = orders.user_id;CROSS JOIN
Cartesian product of all rows
SELECT colors.name AS color, sizes.name AS sizeFROM colorsCROSS JOIN sizes;Self JOIN
Join table to itself
SELECT e.name AS employee, m.name AS managerFROM employees eLEFT JOIN employees m ON e.manager_id = m.id;Multiple JOINs
Join more than two tables
SELECT users.name, orders.id AS order_id, products.name AS productFROM usersINNER JOIN orders ON users.id = orders.user_idINNER JOIN order_items ON orders.id = order_items.order_idINNER JOIN products ON order_items.product_id = products.id;JOIN with USING
Shorthand when column names match
SELECT users.name, orders.totalFROM usersINNER JOIN orders USING (user_id);NATURAL JOIN
Auto-join on matching column names
-- Joins on all columns with same nameSELECT * FROM ordersNATURAL JOIN order_items; -- Be careful: joins on ALL matching names-- Prefer explicit JOIN conditionsSubqueries
Nested queries within queries
Subquery in WHERE
Use query result as filter condition
SELECT * FROM productsWHERE price > ( SELECT AVG(price) FROM products);Subquery with IN
Match against subquery results
SELECT * FROM usersWHERE id IN ( SELECT user_id FROM orders WHERE total > 1000);Subquery in FROM
Use subquery as a derived table
SELECT country, avg_orderFROM ( SELECT u.country, AVG(o.total) AS avg_order FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.country) AS country_statsWHERE avg_order > 500;Scalar Subquery in SELECT
Return single value in column
SELECT name, price, price - (SELECT AVG(price) FROM products) AS diff_from_avgFROM products;Correlated Subquery
Subquery referencing outer query
SELECT * FROM products pWHERE price > ( SELECT AVG(price) FROM products WHERE category_id = p.category_id);LATERAL Join
Subquery can reference preceding tables
SELECT u.name, recent.order_date, recent.totalFROM users uCROSS JOIN LATERAL ( SELECT order_date, total FROM orders WHERE user_id = u.id ORDER BY order_date DESC LIMIT 3) AS recent;NOT EXISTS
Check subquery returns no rows
-- Users with no ordersSELECT * FROM users uWHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id);CTEs
Common Table Expressions (WITH)
Basic CTE
Define a named subquery
WITH active_users AS ( SELECT * FROM users WHERE status = 'active')SELECT * FROM active_usersWHERE created_at > '2024-01-01';Multiple CTEs
Chain multiple CTEs together
WITH active_users AS ( SELECT * FROM users WHERE status = 'active' ), user_orders AS ( SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id )SELECT u.name, COALESCE(o.order_count, 0) AS ordersFROM active_users uLEFT JOIN user_orders o ON u.id = o.user_id;Recursive CTE
Query hierarchical or tree data
WITH RECURSIVE org_chart AS ( -- Base case: top-level (no manager) SELECT id, name, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL -- Recursive case SELECT e.id, e.name, e.manager_id, oc.level + 1 FROM employees e INNER JOIN org_chart oc ON e.manager_id = oc.id)SELECT * FROM org_chart ORDER BY level, name;Generate Number Series
Create sequence with recursive CTE
WITH RECURSIVE numbers AS ( SELECT 1 AS n UNION ALL SELECT n + 1 FROM numbers WHERE n < 100)SELECT * FROM numbers;Window Functions
ROW_NUMBER, RANK, LAG, LEAD
ROW_NUMBER
Assign sequential numbers to rows
SELECT name, price, ROW_NUMBER() OVER (ORDER BY price DESC) AS rankFROM products;RANK vs DENSE_RANK
Handle ties differently
SELECT name, price, RANK() OVER (ORDER BY price DESC) AS rank, DENSE_RANK() OVER (ORDER BY price DESC) AS dense_rankFROM products;PARTITION BY
Window function per group
SELECT category, name, price, RANK() OVER (PARTITION BY category ORDER BY price DESC) AS category_rankFROM products;LAG and LEAD
Access previous/next row values
SELECT order_date, total, LAG(total) OVER (ORDER BY order_date) AS prev_total, LEAD(total) OVER (ORDER BY order_date) AS next_totalFROM orders;Running Total
Cumulative sum with SUM window
SELECT order_date, total, SUM(total) OVER (ORDER BY order_date) AS running_totalFROM orders;Moving Average
Average over sliding window
SELECT order_date, total, AVG(total) OVER ( ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS moving_avg_7dayFROM orders;FIRST_VALUE / LAST_VALUE
Get first or last value in window
SELECT name, price, FIRST_VALUE(name) OVER (ORDER BY price DESC) AS most_expensive, LAST_VALUE(name) OVER ( ORDER BY price DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS cheapestFROM products;NTILE Buckets
Divide rows into N equal groups
SELECT name, price, NTILE(4) OVER (ORDER BY price) AS quartileFROM products;String Functions
Text manipulation functions
CONCAT / ||
Join strings together
SELECT CONCAT(first_name, ' ', last_name) AS full_name, first_name || ' ' || last_name AS full_name_altFROM users;UPPER / LOWER
Change case
SELECT UPPER(name) AS uppercase, LOWER(email) AS lowercase, INITCAP(name) AS titlecaseFROM users;SUBSTRING
Extract part of string
SELECT SUBSTRING(phone FROM 1 FOR 3) AS area_code, SUBSTRING(email FROM '@(.+)$') AS domainFROM users;TRIM / LTRIM / RTRIM
Remove whitespace or characters
SELECT TRIM(' hello ') AS trimmed, LTRIM(' hello ') AS left_trimmed, RTRIM(' hello ') AS right_trimmed, TRIM(BOTH '-' FROM '--hello--') AS custom_trimFROM users;REPLACE
Replace substring with another
SELECT REPLACE(phone, '-', '') AS phone_digits, REPLACE(name, 'Mr. ', '') AS name_no_titleFROM users;SPLIT_PART
Split string and get part
SELECT email, SPLIT_PART(email, '@', 1) AS username, SPLIT_PART(email, '@', 2) AS domainFROM users;LENGTH / CHAR_LENGTH
Get string length
SELECT name, LENGTH(name) AS name_length, CHAR_LENGTH(name) AS char_countFROM users;POSITION / STRPOS
Find substring position
SELECT email, POSITION('@' IN email) AS at_position, STRPOS(email, '@') AS at_position_altFROM users;Date & Time
Date and time functions
Current Date/Time
Get current timestamp
SELECT CURRENT_DATE AS today, CURRENT_TIME AS now_time, CURRENT_TIMESTAMP AS now_full, NOW() AS now_func;EXTRACT Parts
Get specific date/time parts
SELECT created_at, EXTRACT(YEAR FROM created_at) AS year, EXTRACT(MONTH FROM created_at) AS month, EXTRACT(DAY FROM created_at) AS day, EXTRACT(HOUR FROM created_at) AS hourFROM orders;DATE_TRUNC
Truncate to specified precision
SELECT created_at, DATE_TRUNC('month', created_at) AS month_start, DATE_TRUNC('year', created_at) AS year_start, DATE_TRUNC('hour', created_at) AS hour_startFROM orders;Date Arithmetic
Add or subtract intervals
SELECT NOW() AS now, NOW() + INTERVAL '1 day' AS tomorrow, NOW() - INTERVAL '1 week' AS last_week, NOW() + INTERVAL '3 months' AS three_monthsFROM orders;AGE Function
Calculate difference between dates
SELECT name, birth_date, AGE(birth_date) AS age, AGE(NOW(), hire_date) AS tenureFROM employees;TO_CHAR Formatting
Format date as string
SELECT created_at, TO_CHAR(created_at, 'YYYY-MM-DD') AS iso_date, TO_CHAR(created_at, 'Mon DD, YYYY') AS pretty_date, TO_CHAR(created_at, 'HH24:MI:SS') AS time_24hFROM orders;Generate Date Series
Create sequence of dates
SELECT date::dateFROM generate_series( '2024-01-01'::date, '2024-12-31'::date, '1 month'::interval) AS date;JSON
JSON data manipulation
Access JSON Fields
Use -> and ->> operators
SELECT data->'name' AS name_json, -- returns JSON data->>'name' AS name_text, -- returns text data->'address'->>'city' AS city -- nested accessFROM users;JSON Path
Use #> and #>> for path access
SELECT data #> '{address,city}' AS city_json, data #>> '{address,city}' AS city_text, data #>> '{tags,0}' AS first_tagFROM users;JSONB Containment
Check if JSON contains value
-- Contains this JSON?SELECT * FROM productsWHERE data @> '{"status": "active"}'; -- Is contained by?SELECT * FROM productsWHERE '{"status": "active", "type": "book"}' @> data;Build JSON Objects
Create JSON from columns
SELECT json_build_object( 'id', id, 'name', name, 'email', email ) AS user_jsonFROM users;JSON Aggregation
Aggregate rows into JSON array
SELECT category, json_agg(name) AS product_names, json_agg(json_build_object('name', name, 'price', price)) AS productsFROM productsGROUP BY category;JSONB_SET Update
Update nested JSON value
UPDATE usersSET data = jsonb_set( data, '{address,city}', '"New York"')WHERE id = 1;INSERT / UPDATE / DELETE
Modify data in tables
Basic INSERT
Insert a single row
INSERT INTO users (name, email, status)VALUES ('John Doe', 'john@example.com', 'active');Insert Multiple Rows
Insert several rows at once
INSERT INTO users (name, email)VALUES ('Alice', 'alice@example.com'), ('Bob', 'bob@example.com'), ('Carol', 'carol@example.com');INSERT with RETURNING
Get inserted data back
INSERT INTO users (name, email)VALUES ('John', 'john@example.com')RETURNING id, name, created_at;INSERT from SELECT
Insert rows from a query
INSERT INTO users_archive (name, email)SELECT name, emailFROM usersWHERE status = 'inactive';UPSERT (ON CONFLICT)
Insert or update if exists
INSERT INTO users (id, name, email)VALUES (1, 'John', 'john@example.com')ON CONFLICT (id) DO UPDATESET name = EXCLUDED.name, email = EXCLUDED.email;INSERT or Ignore
Skip if already exists
INSERT INTO users (email, name)VALUES ('john@example.com', 'John')ON CONFLICT (email) DO NOTHING;Basic UPDATE
Update rows matching condition
UPDATE usersSET status = 'inactive', updated_at = NOW()WHERE last_login < '2024-01-01';UPDATE with RETURNING
Get updated rows back
UPDATE productsSET price = price * 1.10WHERE category = 'electronics'RETURNING id, name, price;UPDATE with FROM
Update using another table
UPDATE ordersSET status = 'shipped'FROM shipmentsWHERE orders.id = shipments.order_id AND shipments.shipped_at IS NOT NULL;Basic DELETE
Delete rows matching condition
DELETE FROM usersWHERE status = 'deleted' AND updated_at < NOW() - INTERVAL '30 days';DELETE with RETURNING
Get deleted rows back
DELETE FROM sessionsWHERE expires_at < NOW()RETURNING id, user_id;TRUNCATE Table
Delete all rows quickly
-- Fast delete all rows (resets sequences)TRUNCATE TABLE logs; -- Truncate multiple tablesTRUNCATE TABLE orders, order_itemsRESTART IDENTITY CASCADE;Tables & DDL
CREATE, ALTER, DROP structures
CREATE TABLE
Create a new table
CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT NOW());CREATE IF NOT EXISTS
Create table only if missing
CREATE TABLE IF NOT EXISTS logs ( id SERIAL PRIMARY KEY, message TEXT, level VARCHAR(20), created_at TIMESTAMP DEFAULT NOW());CREATE TABLE AS
Create table from query
CREATE TABLE active_users ASSELECT id, name, emailFROM usersWHERE status = 'active';Add Column
Add a new column to table
ALTER TABLE usersADD COLUMN phone VARCHAR(20); -- With default valueALTER TABLE usersADD COLUMN is_verified BOOLEAN DEFAULT false;Drop Column
Remove a column from table
ALTER TABLE usersDROP COLUMN phone; -- Drop if existsALTER TABLE usersDROP COLUMN IF EXISTS temp_field;Rename Column / Table
Rename columns or tables
-- Rename columnALTER TABLE usersRENAME COLUMN name TO full_name; -- Rename tableALTER TABLE usersRENAME TO customers;Change Column Type
Modify column data type
ALTER TABLE productsALTER COLUMN price TYPE NUMERIC(10,2); -- With conversionALTER TABLE usersALTER COLUMN age TYPE INTEGERUSING age::INTEGER;Set / Drop Default
Change column default value
-- Set defaultALTER TABLE usersALTER COLUMN status SET DEFAULT 'pending'; -- Remove defaultALTER TABLE usersALTER COLUMN status DROP DEFAULT;DROP TABLE
Delete a table
DROP TABLE users; -- Drop if existsDROP TABLE IF EXISTS temp_users; -- Drop with dependent objectsDROP TABLE orders CASCADE;CREATE INDEX
Create index for faster queries
-- Basic indexCREATE INDEX idx_users_email ON users(email); -- Unique indexCREATE UNIQUE INDEX idx_users_username ON users(username); -- Multi-column indexCREATE INDEX idx_orders_user_date ON orders(user_id, created_at);DROP INDEX
Remove an index
DROP INDEX idx_users_email; DROP INDEX IF EXISTS idx_temp;CREATE VIEW
Create a virtual table
CREATE VIEW active_orders ASSELECT o.id, u.name, o.total, o.created_atFROM orders oJOIN users u ON o.user_id = u.idWHERE o.status = 'active'; -- Then query like a tableSELECT * FROM active_orders;Constraints
PRIMARY KEY, FOREIGN KEY, UNIQUE
PRIMARY KEY
Unique identifier for rows
-- Inline definitionCREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100)); -- Named constraintCREATE TABLE products ( id INTEGER, sku VARCHAR(50), CONSTRAINT pk_products PRIMARY KEY (id));FOREIGN KEY
Reference another table
CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), total DECIMAL(10,2)); -- With ON DELETE actionCREATE TABLE comments ( id SERIAL PRIMARY KEY, post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE, content TEXT);UNIQUE Constraint
Prevent duplicate values
-- Single columnCREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE); -- Multiple columnsCREATE TABLE memberships ( user_id INTEGER, group_id INTEGER, UNIQUE (user_id, group_id));NOT NULL
Require a value
CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL);CHECK Constraint
Validate column values
CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(100), price DECIMAL(10,2) CHECK (price > 0), quantity INTEGER CHECK (quantity >= 0)); -- Named checkCREATE TABLE users ( age INTEGER, CONSTRAINT valid_age CHECK (age >= 0 AND age < 150));DEFAULT Value
Set automatic default
CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR(200), status VARCHAR(20) DEFAULT 'draft', views INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW());Add Constraint to Existing Table
Add constraints with ALTER
-- Add foreign keyALTER TABLE ordersADD CONSTRAINT fk_orders_userFOREIGN KEY (user_id) REFERENCES users(id); -- Add unique constraintALTER TABLE usersADD CONSTRAINT unique_email UNIQUE (email); -- Add check constraintALTER TABLE productsADD CONSTRAINT positive_price CHECK (price > 0);Drop Constraint
Remove a constraint
ALTER TABLE ordersDROP CONSTRAINT fk_orders_user; ALTER TABLE usersDROP CONSTRAINT IF EXISTS unique_email;Set Operations
UNION, INTERSECT, EXCEPT
UNION
Combine results, remove duplicates
SELECT name, email FROM customersUNIONSELECT name, email FROM suppliers;UNION ALL
Combine results, keep duplicates
SELECT product_id, 'sale' AS type FROM salesUNION ALLSELECT product_id, 'return' AS type FROM returns;INTERSECT
Rows in both queries
SELECT user_id FROM premium_usersINTERSECTSELECT user_id FROM active_users;EXCEPT
Rows in first but not second
SELECT user_id FROM all_usersEXCEPTSELECT user_id FROM banned_users;ORDER with Set Operations
Sort combined results
(SELECT name, 'customer' AS type FROM customers)UNION(SELECT name, 'supplier' AS type FROM suppliers)ORDER BY name;Type Casting
Convert between data types
:: Operator
PostgreSQL cast shorthand
SELECT '123'::INTEGER AS num, '2024-01-15'::DATE AS date, 123::TEXT AS str, '3.14'::NUMERIC AS decimal;CAST Function
Standard SQL casting
SELECT CAST('123' AS INTEGER) AS num, CAST('2024-01-15' AS DATE) AS date, CAST(price AS INTEGER) AS roundedFROM products;Numeric Conversions
Convert between number types
SELECT 1234.567::INTEGER AS truncated, -- 1234 1234.567::NUMERIC(10,1) AS rounded, -- 1234.6 '99.99'::DECIMAL(5,2) AS decimal;Date/Time Conversions
Convert date and time types
SELECT NOW()::DATE AS today, NOW()::TIME AS current_time, '2024-01-15 10:30:00'::TIMESTAMP AS ts, EXTRACT(EPOCH FROM NOW())::INTEGER AS unix;Boolean Conversions
Convert to/from boolean
SELECT 'true'::BOOLEAN, 'yes'::BOOLEAN, '1'::BOOLEAN, 1::BOOLEAN, true::INTEGER; -- 1JSON Conversions
Convert to/from JSON
SELECT '{"name": "John"}'::JSON, '{"name": "John"}'::JSONB, row_to_json(users.*) FROM users;Numeric Functions
ROUND, ABS, CEIL, FLOOR
ROUND
Round to decimal places
SELECT ROUND(3.14159) AS whole, -- 3 ROUND(3.14159, 2) AS two_dec, -- 3.14 ROUND(1234.5, -2) AS hundreds; -- 1200CEIL / FLOOR
Round up or down
SELECT CEIL(4.2) AS ceiling, -- 5 FLOOR(4.8) AS floor, -- 4 CEIL(-4.2) AS neg_ceil, -- -4 FLOOR(-4.8) AS neg_fl; -- -5TRUNC
Truncate toward zero
SELECT TRUNC(4.9) AS truncated, -- 4 TRUNC(-4.9) AS neg_trunc, -- -4 TRUNC(123.456, 1) AS one_dec; -- 123.4ABS
Absolute value
SELECT ABS(-5) AS positive, -- 5 ABS(5) AS still_pos, -- 5 ABS(price - 100) AS diffFROM products;POWER / SQRT
Exponents and square root
SELECT POWER(2, 3) AS eight, -- 8 POWER(10, 2) AS hundred, -- 100 SQRT(16) AS four, -- 4 SQRT(2) AS root2; -- 1.414...MOD / %
Remainder after division
SELECT MOD(10, 3) AS remainder, -- 1 17 % 5 AS mod_operator, -- 2 MOD(id, 2) AS is_oddFROM users;RANDOM
Generate random numbers
SELECT RANDOM() AS zero_to_one, FLOOR(RANDOM() * 100) AS zero_to_99, FLOOR(RANDOM() * (max - min + 1) + min) AS range; -- Random rowSELECT * FROM usersORDER BY RANDOM()LIMIT 1;SIGN / DIV
Sign and integer division
SELECT SIGN(-5) AS negative, -- -1 SIGN(0) AS zero, -- 0 SIGN(5) AS positive, -- 1 DIV(17, 5) AS int_div; -- 3Arrays
PostgreSQL array operations
Array Literals
Create array values
SELECT ARRAY[1, 2, 3] AS int_array, ARRAY['a', 'b', 'c'] AS text_array, '{1,2,3}'::INTEGER[] AS cast_array;Access Array Elements
Get elements by index (1-based)
SELECT (ARRAY['a','b','c'])[1] AS first, -- 'a' (ARRAY['a','b','c'])[2:3] AS slice, -- {'b','c'} tags[1] AS first_tagFROM posts;ANY / ALL with Arrays
Compare against array values
-- Match any value in arraySELECT * FROM usersWHERE status = ANY(ARRAY['active', 'pending']); -- Match all valuesSELECT * FROM productsWHERE price > ALL(ARRAY[10, 20, 30]);Array Contains
Check array containment
-- Contains all elementsSELECT * FROM postsWHERE tags @> ARRAY['sql', 'database']; -- Is contained bySELECT * FROM postsWHERE tags <@ ARRAY['sql', 'database', 'postgresql']; -- Overlaps (has common elements)SELECT * FROM postsWHERE tags && ARRAY['sql', 'nosql'];ARRAY_AGG
Aggregate values into array
SELECT category, ARRAY_AGG(name) AS product_names, ARRAY_AGG(DISTINCT status) AS statusesFROM productsGROUP BY category;UNNEST
Expand array to rows
SELECT UNNEST(ARRAY[1, 2, 3]) AS num; -- Expand column arraySELECT id, UNNEST(tags) AS tagFROM posts;Array Functions
Common array operations
SELECT ARRAY_LENGTH(ARRAY[1,2,3], 1) AS len, -- 3 ARRAY_CAT(ARRAY[1,2], ARRAY[3,4]) AS concat, -- {1,2,3,4} ARRAY_APPEND(ARRAY[1,2], 3) AS append, -- {1,2,3} ARRAY_REMOVE(ARRAY[1,2,3], 2) AS removed; -- {1,3}Array Search
Find elements in arrays
SELECT ARRAY_POSITION(ARRAY['a','b','c'], 'b') AS pos, -- 2 'b' = ANY(ARRAY['a','b','c']) AS exists, -- true CARDINALITY(ARRAY[1,2,3]) AS size; -- 3