SQL Cheatsheet

Creating Tables

Use this SQL reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

CREATE TABLE Syntax

CREATE TABLE table_name (
    column_name data_type [column_constraints] [DEFAULT value],
    ...,
    [table_constraints]
);
-- Minimal example
CREATE TABLE products (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(255)   NOT NULL,
    price       DECIMAL(10,2)  NOT NULL DEFAULT 0.00,
    description TEXT,
    created_at  TIMESTAMP      NOT NULL DEFAULT NOW()
);

-- With table-level constraints
CREATE TABLE order_items (
    order_id   INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    qty        INTEGER NOT NULL CHECK (qty > 0),
    unit_price DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id)   REFERENCES orders(id)   ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE RESTRICT
);

Column Data Types

Integers

id SMALLINT,          -- 2 bytes, -32768 to 32767
id INTEGER,           -- 4 bytes, ~±2.1 billion
id BIGINT,            -- 8 bytes, ~±9.2 × 10^18
id SERIAL,            -- auto-increment INTEGER (PostgreSQL)
id BIGSERIAL,         -- auto-increment BIGINT (PostgreSQL)
id INTEGER GENERATED ALWAYS AS IDENTITY,    -- SQL standard auto-increment
id INTEGER GENERATED BY DEFAULT AS IDENTITY -- allows manual override

Exact Numerics

price DECIMAL(10, 2),   -- exact: 10 total digits, 2 after decimal
amount NUMERIC(15, 4),  -- same as DECIMAL

Approximate Numerics

ratio REAL,             -- 4-byte float (~6 decimal digits)
score DOUBLE PRECISION, -- 8-byte float (~15 decimal digits)
value FLOAT(24),        -- at least 24 bits of mantissa

Strings

code   CHAR(5),         -- fixed-length, padded with spaces
name   VARCHAR(255),    -- variable-length, max 255 chars
bio    TEXT,            -- unlimited length
notes  NVARCHAR(200),   -- Unicode variable string (SQL Server)

Date and Time

dob        DATE,                         -- '2024-06-15'
start_time TIME,                         -- '14:30:00'
created_at TIMESTAMP,                    -- '2024-06-15 14:30:00' (no tz)
updated_at TIMESTAMPTZ,                  -- with time zone (PostgreSQL)
updated_at TIMESTAMP WITH TIME ZONE,     -- SQL standard
duration   INTERVAL,                     -- '3 days 4 hours' (PostgreSQL)

Boolean

active BOOLEAN DEFAULT TRUE,  -- TRUE / FALSE / NULL
-- MySQL uses TINYINT(1); SQLite uses INTEGER 0/1

Special Types

-- UUID
id UUID DEFAULT gen_random_uuid(),    -- PostgreSQL 13+
id UUID DEFAULT uuid_generate_v4(),   -- with uuid-ossp extension

-- JSON
metadata JSON,           -- stored as text
metadata JSONB,          -- binary JSON, indexed (PostgreSQL)

-- Arrays (PostgreSQL)
tags TEXT[],
scores INTEGER[],

-- Enum
status VARCHAR(20) CHECK (status IN ('pending','active','cancelled')),
-- Or MySQL ENUM type:
status ENUM('pending','active','cancelled'),
-- Or PostgreSQL custom type:
CREATE TYPE order_status AS ENUM ('pending', 'active', 'cancelled');
-- then:
status order_status,

Auto-Increment Primary Keys

-- PostgreSQL (SERIAL shorthand)
id SERIAL PRIMARY KEY,
id BIGSERIAL PRIMARY KEY,

-- PostgreSQL (SQL:2003 IDENTITY)
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
id BIGINT  GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,

-- MySQL
id INT AUTO_INCREMENT PRIMARY KEY,
id BIGINT AUTO_INCREMENT PRIMARY KEY,

-- SQLite
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- (without AUTOINCREMENT, SQLite reuses deleted max+1 by default)

-- SQL Server
id INT IDENTITY(1,1) PRIMARY KEY,   -- starts at 1, increments by 1
id INT IDENTITY(100,5) PRIMARY KEY, -- starts at 100, increments by 5

DEFAULT Values

created_at  TIMESTAMP   DEFAULT NOW(),
created_at  TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
status      VARCHAR(20) DEFAULT 'pending',
score       INTEGER     DEFAULT 0,
is_active   BOOLEAN     DEFAULT TRUE,
uuid_col    UUID        DEFAULT gen_random_uuid(),

-- Computed default (PostgreSQL generated column)
full_name   TEXT GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED,

-- SQL Server computed column
full_name   AS (first_name + ' ' + last_name) PERSISTED,

IF NOT EXISTS

CREATE TABLE IF NOT EXISTS users (
    id   SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);
-- No error if the table already exists; schema is NOT updated

-- Drop if exists
DROP TABLE IF EXISTS temp_results;

ALTER TABLE

-- Add a column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users ADD COLUMN verified BOOLEAN NOT NULL DEFAULT FALSE;

-- Remove a column
ALTER TABLE users DROP COLUMN phone;
ALTER TABLE users DROP COLUMN IF EXISTS phone;

-- Rename a column
ALTER TABLE users RENAME COLUMN username TO login_name;

-- Change column type (PostgreSQL)
ALTER TABLE products ALTER COLUMN price TYPE NUMERIC(12, 4);

-- Change column type (MySQL)
ALTER TABLE products MODIFY COLUMN price DECIMAL(12, 4) NOT NULL;

-- Set / drop default
ALTER TABLE users ALTER COLUMN status SET DEFAULT 'active';
ALTER TABLE users ALTER COLUMN status DROP DEFAULT;

-- Set / drop NOT NULL
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
ALTER TABLE users ALTER COLUMN email DROP NOT NULL;

-- Rename table
ALTER TABLE orders RENAME TO purchase_orders;

-- Add constraint
ALTER TABLE users ADD CONSTRAINT uq_email UNIQUE (email);
ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE products ADD CONSTRAINT chk_price CHECK (price >= 0);

-- Drop constraint
ALTER TABLE users DROP CONSTRAINT uq_email;
ALTER TABLE users DROP CONSTRAINT IF EXISTS uq_email;

DROP TABLE

-- Drop table (error if it doesn't exist)
DROP TABLE products;

-- Drop table if it exists
DROP TABLE IF EXISTS products;

-- Drop multiple tables
DROP TABLE IF EXISTS order_items, orders, products;

-- Drop with CASCADE (also drops dependent views, FKs, etc.)
DROP TABLE users CASCADE;

-- DROP TABLE vs TRUNCATE vs DELETE:
-- DROP TABLE: removes the table and all data permanently
-- TRUNCATE: removes all rows, keeps the table structure
-- DELETE: removes rows, can be filtered, is transactional

CREATE TABLE AS (CTAS)

Create a new table from the result of a query.

-- Creates table with columns inferred from query (no constraints/indexes)
CREATE TABLE top_customers AS
SELECT user_id, SUM(total) AS lifetime_value
FROM orders
WHERE status = 'completed'
GROUP BY user_id
HAVING SUM(total) > 1000;

-- PostgreSQL: WITH NO DATA (creates structure only)
CREATE TABLE orders_backup AS
SELECT * FROM orders WITH NO DATA;

-- Then populate later:
INSERT INTO orders_backup SELECT * FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2023;

-- MySQL equivalent
CREATE TABLE orders_archive AS SELECT * FROM orders WHERE 1 = 0;  -- empty

Temporary Tables

Exist only for the duration of the session or transaction.

-- Session-scoped (most DBs)
CREATE TEMPORARY TABLE temp_results (
    id      INTEGER,
    score   DECIMAL(5,2)
);

-- PostgreSQL: also ON COMMIT DROP / DELETE ROWS / PRESERVE ROWS
CREATE TEMP TABLE batch_staging (
    raw_data TEXT
) ON COMMIT DROP;           -- auto-dropped at end of transaction

-- MySQL: same syntax as above
-- SQL Server: # prefix for local, ## for global temp tables
CREATE TABLE #temp_results (id INT, score FLOAT);
SELECT * FROM #temp_results;

Table Inheritance and Partitioning

-- PostgreSQL table partitioning (declarative, Postgres 10+)
CREATE TABLE orders (
    id         BIGSERIAL,
    created_at DATE NOT NULL,
    user_id    INTEGER,
    total      DECIMAL(12,2)
) PARTITION BY RANGE (created_at);

-- Create partitions
CREATE TABLE orders_2024 PARTITION OF orders
    FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');

CREATE TABLE orders_2025 PARTITION OF orders
    FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');

-- List partitioning
CREATE TABLE customers (
    id     SERIAL,
    region TEXT NOT NULL
) PARTITION BY LIST (region);

CREATE TABLE customers_us PARTITION OF customers FOR VALUES IN ('US', 'CA');
CREATE TABLE customers_eu PARTITION OF customers FOR VALUES IN ('UK', 'DE', 'FR');