DML — Data Manipulation Language
SELECT — Query with filters, sorting, grouping
SELECT
    u.id,
    u.name,
    u.email,
    COUNT(o.id)     AS order_count,
    SUM(o.total)   AS total_spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE
    u.created_at >= '2024-01-01'
    AND u.status = 'active'
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > 0
ORDER BY total_spent DESC
LIMIT 25 OFFSET 0;
INSERT — Add rows
-- Single row
INSERT INTO products (name, price, category_id, stock)
VALUES ('Mechanical Keyboard', 89.99, 4, 150);

-- Multiple rows
INSERT INTO tags (name, color)
VALUES
    ('backend', '#0084ff'),
    ('frontend', '#00d4aa'),
    ('database', '#ffa500');

-- Insert from SELECT
INSERT INTO archive_orders
SELECT * FROM orders
WHERE created_at < '2023-01-01';
UPDATE — Modify rows
UPDATE products
SET
    price = price * 0.9,          -- 10% discount
    updated_at = NOW()
WHERE
    category_id = 4
    AND stock > 0
    AND sale_end IS NULL;
DELETE — Remove rows
-- Delete with condition
DELETE FROM sessions
WHERE expires_at < NOW();

-- Delete with subquery
DELETE FROM users
WHERE id IN (
    SELECT user_id FROM banned_users
    WHERE reason = 'spam'
);
DDL — Data Definition Language
CREATE TABLE
CREATE TABLE orders (
    id           BIGSERIAL          PRIMARY KEY,
    user_id      BIGINT             NOT NULL
                                       REFERENCES users(id),
    status       VARCHAR(20)        NOT NULL
                                       DEFAULT 'pending',
    total        DECIMAL(12,2)     NOT NULL
                                       CHECK (total >= 0),
    metadata     JSONB,
    created_at   TIMESTAMP          NOT NULL
                                       DEFAULT NOW(),
    updated_at   TIMESTAMP          NOT NULL
                                       DEFAULT NOW()
);

CREATE INDEX idx_orders_user_status
    ON orders (user_id, status);

CREATE INDEX idx_orders_created
    ON orders (created_at DESC);
ALTER TABLE — Schema modifications
ALTER TABLE users
    ADD COLUMN phone       VARCHAR(20),
    ADD COLUMN verified_at TIMESTAMP,
    ALTER COLUMN email     SET NOT NULL,
    DROP COLUMN legacy_id;

ALTER TABLE users
    RENAME COLUMN username TO handle;
CREATE VIEW
CREATE VIEW active_user_summary AS
SELECT
    u.id,
    u.name,
    COUNT(o.id)   AS orders,
    MAX(o.created_at) AS last_order
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.status = 'active'
GROUP BY u.id, u.name;
CTE — Common Table Expressions & Subqueries
WITH — Common Table Expression (CTE)
WITH
monthly_revenue AS (
    SELECT
        DATE_TRUNC('month', created_at) AS month,
        SUM(total)                          AS revenue
    FROM orders
    WHERE status = 'completed'
    GROUP BY 1
),
ranked AS (
    SELECT
        month, revenue,
        LAG(revenue) OVER (ORDER BY month) AS prev_revenue
    FROM monthly_revenue
)
SELECT
    month,
    revenue,
    ROUND(((revenue - prev_revenue) / prev_revenue) * 100, 2) AS growth_pct
FROM ranked
ORDER BY month;
EXPLAIN — Query plan analysis
EXPLAIN ANALYZE
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON o.user_id = u.id
GROUP BY u.id;

-- Seq Scan vs Index Scan vs Hash Join
-- Look for: rows estimate vs actual, loops, buffers hit
-- Add index if seq scan on large table
TCL — Transaction Control Language
BEGIN;                              -- Start transaction

UPDATE accounts SET balance = balance - 500
WHERE id = 1;

UPDATE accounts SET balance = balance + 500
WHERE id = 2;

SAVEPOINT transfer_done;

-- If something goes wrong:
ROLLBACK TO transfer_done;

-- On success:
COMMIT;
SQL Data Types
Numeric Types
TINYINT-128 to 1271 byte
SMALLINT-32K to 32K2 bytes
INT / INTEGER-2B to 2B4 bytes
BIGINT-9.2×10¹⁸8 bytes
DECIMAL(p,s)Exact decimalVariable
FLOAT / REAL~7 sig digits4 bytes
DOUBLE~15 sig digits8 bytes
BOOLEANtrue / false1 byte
SERIALAuto-increment INTPostgreSQL
BIGSERIALAuto-increment BIGINTPostgreSQL
String / Text Types
CHAR(n)Fixed-lengthPadded with spaces
VARCHAR(n)Variable-lengthMax n characters
TEXTUnlimited stringNo length limit
TINYTEXT255 charsMySQL only
MEDIUMTEXT16MBMySQL only
LONGTEXT4GBMySQL only
BLOBBinary large objectBinary data
BYTEABinary dataPostgreSQL
ENUM('a','b')One of defined valuesMySQL/PG
SETMultiple enum valuesMySQL
Date & Time Types
DATEYYYY-MM-DD3 bytes
TIMEHH:MM:SS3–5 bytes
DATETIMEDate + TimeNo timezone
TIMESTAMPUnix epochWith/without TZ
TIMESTAMPTZTimestamp + TZPostgreSQL
INTERVALDuration'3 months 2 days'
YEAR1901–2155MySQL
JSON & Structured Types
JSONText JSONValidates syntax
JSONBBinary JSONIndexable, fast
ARRAYTyped arraysPostgreSQL: int[]
HSTOREKey-value storePostgreSQL ext
XMLXML documentWith XPath support
Network & Special Types
UUID128-bit identifiergen_random_uuid()
INETIPv4/IPv6 addressPostgreSQL
CIDRNetwork address blockPostgreSQL
MACADDRMAC addressPostgreSQL
POINTGeometric point (x,y)PostgreSQL
TSRANGETimestamp rangePostgreSQL
MONEYCurrency amountLocale-dependent
Compatibility Matrix
TypePostgreSQLMySQLSQLiteNotes
INT / BIGINTUniversal support
DECIMAL / NUMERIC~SQLite uses REAL
TEXT / VARCHARSQLite: TEXT for all
BOOLEANTINYINTINTEGERPG has native bool
TIMESTAMPTZPostgreSQL-specific
JSONBPG only; MySQL has JSON
ARRAYPostgreSQL-specific
UUIDTEXTSQLite stores as text
ENUMUse CHECK in SQLite
INET / CIDRPostgreSQL network types
AUTO_INCREMENTSERIAL/IDENTITYROWIDDifferent syntax
SQL Functions
Aggregate Functions
    COUNTrows
    COUNT(*) / COUNT(col) / COUNT(DISTINCT col)
    SELECT COUNT(DISTINCT user_id) FROM orders;
    Counts rows. COUNT(*) includes NULLs; COUNT(col) excludes them; DISTINCT counts unique values.
    SUMtotal
    SUM(expression)
    SELECT SUM(total) FROM orders WHERE status='paid';
    Returns the sum of non-NULL values. Returns NULL if all values are NULL.
    AVGmean
    AVG(expression)
    SELECT AVG(price) FROM products WHERE active=1;
    Returns the arithmetic mean of non-NULL values.
    MIN / MAXextremes
    MIN(col), MAX(col)
    SELECT MIN(price), MAX(price) FROM products;
    Returns the minimum or maximum value. Works on numbers, strings, and dates.
    GROUP_CONCATMySQL
    GROUP_CONCAT(col SEPARATOR ',')
    SELECT GROUP_CONCAT(tag ORDER BY tag) FROM post_tags GROUP BY post_id;
    Concatenates group values into a string. PostgreSQL equivalent: STRING_AGG(col, ',').
String Functions
    UPPER / LOWER
    UPPER(str), LOWER(str)
    SELECT UPPER('hello world'); → HELLO WORLD
    Converts string to uppercase or lowercase.
    LENGTH / CHAR_LENGTH
    LENGTH(str), CHAR_LENGTH(str)
    SELECT LENGTH('café'); → 5 (bytes) vs 4 (chars)
    Returns byte length or character count (important for multibyte encodings).
    SUBSTR / SUBSTRING
    SUBSTRING(str FROM pos FOR len)
    SELECT SUBSTRING('Hello World' FROM 7 FOR 5); → World
    Extracts a substring. 1-indexed. Negative pos counts from end (MySQL).
    CONCAT / ||
    CONCAT(s1, s2, ...) or s1 || s2
    SELECT first_name || ' ' || last_name AS full_name;
    Concatenates strings. CONCAT ignores NULLs; || propagates them (use COALESCE).
    REPLACE
    REPLACE(str, from_str, to_str)
    SELECT REPLACE(phone, '-', ''); → no hyphens
    Replaces all occurrences of a substring within a string.
    TRIM / LTRIM / RTRIM
    TRIM([BOTH|LEADING|TRAILING] chars FROM str)
    SELECT TRIM(' hello '); → 'hello'
    Removes leading/trailing characters (default: spaces).
    LIKE / ILIKE
    col LIKE 'pattern%' (ILIKE = case-insensitive)
    WHERE email ILIKE '%@gmail.com'
    Pattern matching. % = any chars, _ = single char. Use indexes for prefix matches only.
    REGEXP / ~
    col REGEXP 'pattern' or col ~ 'pattern'
    WHERE phone ~ '^\+1[0-9]{10}$'
    Regular expression matching. ~ in PostgreSQL; REGEXP in MySQL.
Date & Time Functions
    NOW / CURRENT_TIMESTAMP
    NOW(), CURRENT_TIMESTAMP
    INSERT INTO logs (created_at) VALUES (NOW());
    Returns the current date and time. NOW() is transaction-stable in PostgreSQL.
    DATE_FORMAT
    DATE_FORMAT(date, '%Y-%m') — MySQL
    SELECT DATE_FORMAT(created_at, '%Y-%m') AS month;
    Formats a date. PostgreSQL uses TO_CHAR(date, 'YYYY-MM').
    DATE_TRUNC
    DATE_TRUNC('month', timestamp) — PostgreSQL
    SELECT DATE_TRUNC('week', NOW()); → start of week
    Truncates timestamp to specified precision. PostgreSQL-specific.
    EXTRACT / DATE_PART
    EXTRACT(YEAR FROM date)
    WHERE EXTRACT(MONTH FROM created_at) = 12
    Extracts a specific part (year, month, day, hour, etc.) from a datetime value.
    DATE_ADD / INTERVAL
    DATE_ADD(date, INTERVAL 7 DAY) or date + INTERVAL '7 days'
    WHERE expires_at > NOW() + INTERVAL '30 days'
    Adds an interval to a date. MySQL uses DATE_ADD(); PostgreSQL uses +INTERVAL.
    DATEDIFF
    DATEDIFF(date1, date2) — MySQL
    SELECT DATEDIFF(NOW(), created_at) AS days_old;
    Returns number of days between two dates. PostgreSQL: (date1 - date2).
Window Functions
    ROW_NUMBER
    ROW_NUMBER() OVER (PARTITION BY col ORDER BY col)
    SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) rn FROM orders;
    Assigns sequential row number within each partition. No ties — each row gets unique number.
    RANK / DENSE_RANK
    RANK() OVER (...), DENSE_RANK() OVER (...)
    SELECT name, score, DENSE_RANK() OVER (ORDER BY score DESC) AS rank;
    RANK skips numbers on ties (1,2,2,4); DENSE_RANK doesn't (1,2,2,3).
    LAG / LEAD
    LAG(col, offset, default) OVER (ORDER BY col)
    SELECT revenue, LAG(revenue) OVER (ORDER BY month) AS prev_month;
    Access previous (LAG) or next (LEAD) row's value without a self-join.
    FIRST_VALUE / LAST_VALUE
    FIRST_VALUE(col) OVER (PARTITION BY x ORDER BY y)
    SELECT *, FIRST_VALUE(price) OVER (PARTITION BY category ORDER BY date) AS initial_price;
    Returns the first/last value in the window frame. Use ROWS BETWEEN for frame control.
    NTILE
    NTILE(n) OVER (ORDER BY col)
    SELECT name, score, NTILE(4) OVER (ORDER BY score DESC) AS quartile;
    Divides rows into n approximately equal buckets. NTILE(100) = percentile.
    SUM / AVG (windowed)
    SUM(col) OVER (PARTITION BY x ORDER BY y ROWS BETWEEN ...)
    SELECT *, SUM(revenue) OVER (ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total;
    Running totals, moving averages. Window frame: ROWS/RANGE BETWEEN.
SQL Joins — Visual Reference
AB
INNER JOIN
Returns only rows where there is a match in BOTH tables. The intersection. Most commonly used join type.
SELECT u.name, o.total FROM users u INNER JOIN orders o ON o.user_id = u.id;
Use case: Get all users who have at least one order
AB
LEFT JOIN
Returns ALL rows from the left table plus matching rows from the right. Non-matching right rows become NULL. The most common join after INNER.
SELECT u.name, o.total FROM users u LEFT JOIN orders o ON o.user_id = u.id; -- NULL total = no orders
Use case: Get all users, even those without orders
AB
RIGHT JOIN
Returns ALL rows from the right table plus matching rows from the left. The mirror image of LEFT JOIN. Often rewritten as a LEFT JOIN for readability.
SELECT u.name, o.total FROM users u RIGHT JOIN orders o ON o.user_id = u.id;
Use case: Get all orders, even orphaned ones
AB
FULL OUTER JOIN
Returns ALL rows from both tables. Where there's no match, the other side becomes NULL. The union of LEFT and RIGHT JOINs.
SELECT u.name, o.total FROM users u FULL OUTER JOIN orders o ON o.user_id = u.id;
Use case: Find mismatches between two datasets
CROSS JOIN
Cartesian product — every row from the left table combined with every row from the right. M×N rows result. Use with care; rarely intentional on large tables.
SELECT sizes.name, colors.name FROM sizes CROSS JOIN colors; -- 4 sizes × 6 colors = 24 rows
Use case: Generate all size/color combinations
empmgr
SELF JOIN
A table joined to itself. Used for hierarchical data, adjacency lists, or finding relationships within the same table. Requires table aliases.
SELECT e.name, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id;
Use case: Employee → Manager hierarchy
Major SQL Databases
PostgreSQL
Open Source
ORDBMS
The world's most advanced open-source relational database. ACID compliant, extensible, supports custom types, functions, operators. Best standards compliance.
JSONBFull-text searchMVCC Window functionsPartitioningExtensions
Used by: Apple, Twitch, Instagram, Spotify, Reddit
MySQL
GPL / Commercial
RDBMS
The world's most popular open-source database, powering most of the web's LAMP stack. Acquired by Oracle. MySQL 8.x added window functions and CTEs.
InnoDB engineReplicationCluster JSON supportWide tooling
Used by: Facebook, Twitter, YouTube, Wikipedia, Airbnb
SQLite
Public Domain
Embedded
Serverless, self-contained, single-file database. No configuration, no daemon. The most widely deployed database in the world — in every phone, browser, and desktop app.
ServerlessZero-configSingle file In-memory modeEmbedded
Used by: Android, iOS, Firefox, Chrome, Python stdlib
MariaDB
GPL
RDBMS
Community-developed MySQL fork by MySQL's original creator (Monty Widenius), created after Oracle's acquisition. Drop-in compatible with MySQL but with extra features.
MySQL compatGalera clusterAria engine Columnstore
Used by: Wikipedia, Google, ServiceNow, Wikimedia
MS SQL Server
Commercial
Enterprise
Microsoft's flagship enterprise RDBMS. Deep Windows/Azure integration. T-SQL dialect. Excellent tooling (SSMS). Industry standard for .NET applications.
T-SQLAzure integrationSSRS/SSIS Always On AGIn-memory OLTP
Used by: Stack Overflow, Dell, Siemens, eBay
Oracle DB
Commercial
Enterprise
The dominant enterprise database for decades. PL/SQL procedural extensions, unmatched scalability, and features for large organizations. Extremely expensive licensing.
PL/SQLRAC clusteringData Guard PartitioningExadata
Used by: Banks, governments, SAP, telcos worldwide
MongoDB
SSPL
Document
Document-oriented NoSQL database. JSON-like BSON documents instead of rows/columns. Flexible schema, horizontal scaling via sharding. Aggregation pipeline for complex queries.
SchemalessShardingAggregation Atlas cloudACID (4.0+)
Used by: Adobe, Expedia, Forbes, Verizon, Bosch
Redis
BSD / RSALv2
Key-Value
In-memory data structure store. Sub-millisecond latency. Supports strings, hashes, lists, sets, sorted sets, streams. Used for caching, sessions, pub/sub, leaderboards.
In-memoryPub/SubSorted sets StreamsLua scriptingCluster
Used by: Twitter, GitHub, Snapchat, Craigslist, Digg
SQL History
Edgar Codd's Relational Model
1970
IBM researcher Edgar F. Codd publishes "A Relational Model of Data for Large Shared Data Banks" in Communications of the ACM. Introduces relations (tables), tuples (rows), attributes (columns), and the concept of a declarative query language. Revolutionizes database theory.
IBM System R & SEQUEL
1974–1977
IBM's System R project at San Jose Research Lab implements Codd's model. Donald Chamberlin and Raymond Boyce design SEQUEL (Structured English Query Language), later renamed SQL to avoid trademark conflict. First relational database prototype demonstrating practical feasibility.
Oracle
1979
Larry Ellison, Bob Miner, and Ed Oates found Relational Software Inc. (later Oracle Corporation) and ship Oracle V2 — the first commercially available SQL RDBMS. Beats IBM's own DB2 to market by reading IBM's System R research papers. Becomes the dominant enterprise database for decades.
IBM DB2
1983
IBM releases DB2 for MVS mainframes. First product to use SQL as its primary interface. Establishes SQL as the de facto standard for relational databases. Still widely used in enterprise environments today.
SQL-86 (ANSI/ISO Standard)
1986
ANSI publishes the first official SQL standard (ANSI X3.135-1986), followed by ISO adoption in 1987. Defines core SQL syntax: SELECT, INSERT, UPDATE, DELETE, CREATE, DROP. Subsequent standards: SQL-89, SQL-92 (major revision — joins, subqueries), SQL:1999 (recursive CTEs, triggers, OOP extensions), SQL:2003 (window functions, XML), SQL:2008, SQL:2011, SQL:2016 (JSON), SQL:2023 (property graph queries).
PostgreSQL (POSTGRES)
1986–1996
Michael Stonebraker at UC Berkeley starts the POSTGRES project as successor to INGRES. Adds object-relational features, complex types, and rules. In 1994, graduate students add SQL support; renamed PostgreSQL 6.0 in 1996. Released under the permissive PostgreSQL License — becomes the world's most advanced open-source RDBMS.
MySQL
1995
Michael Widenius (Monty) and David Axmark release MySQL 1.0 in Sweden. Designed for speed on the web stack (LAMP). Becomes the most popular open-source database by the late 1990s. Sun Microsystems acquires MySQL AB in 2008 for $1B; Oracle acquires Sun in 2010. Widenius forks it as MariaDB to preserve open-source development.
SQLite
2000
D. Richard Hipp creates SQLite for use in the U.S. Navy's guided missile destroyer program — needs a database with no server process. Released as public domain. Becomes the most widely deployed database engine in the world: every Android, iOS, macOS, Windows device, every Firefox, Chrome, Safari browser, every Python installation ships SQLite. Over 1 trillion SQLite databases in active use.
NoSQL Movement
2007–2012
Google's Bigtable (2006) and Amazon's Dynamo (2007) papers inspire a wave of non-relational databases: MongoDB (document), Cassandra (wide-column), Redis (key-value), CouchDB, Neo4j (graph). The term "NoSQL" coined in 2009. Drives massive adoption for web-scale applications needing horizontal sharding. SQL databases respond by adding JSON support and scaling improvements.
Modern SQL Renaissance
2015–present
SQL sees a renaissance: PostgreSQL gains JSONB, full-text search, and logical replication. CockroachDB and TiDB bring distributed NewSQL. DuckDB (2019) pioneers fast analytical SQL in-process. ClickHouse scales to petabytes. SQLite gets WAL mode and JSON functions. SQL:2016 standardizes JSON; SQL:2023 adds property graph queries. AI assistants make SQL more accessible than ever — yet SQL turns 50 still the language of data.
Database Timeline
Database Year Type Creator Notable For
Oracle 1979 Commercial RDBMS Larry Ellison First commercial SQL database
DB2 1983 Commercial RDBMS IBM Enterprise mainframes
SQL Server 1989 Commercial RDBMS Microsoft / Sybase Windows enterprise standard
MySQL 1995 Open Source Widenius / Axmark Web applications, LAMP stack
PostgreSQL 1996 Open Source UC Berkeley Standards compliance, extensibility
SQLite 2000 Embedded D. Richard Hipp Most deployed database engine ever
Key People
Edgar F. Codd
IBM researcher who invented the relational model in 1970. His CACM paper "A Relational Model of Data for Large Shared Data Banks" is the direct intellectual foundation of every SQL database ever built. Winner of the 1981 Turing Award.
Donald Chamberlin
IBM researcher who co-designed SEQUEL (later SQL) with Raymond Boyce during the System R project in 1973–1974. Aimed to make relational data accessible without a computer science background. Also contributed to XQuery.
Larry Ellison
Co-founded Oracle Corporation and shipped the first commercially available SQL relational database in 1979, beating IBM to market. Oracle grew into the dominant enterprise database company of the 1980s–2000s and remains a major player today.
Michael Stonebraker
UC Berkeley professor who led the INGRES and POSTGRES projects, directly giving rise to PostgreSQL. Winner of the 2014 Turing Award for fundamental contributions to the concepts and practices underlying modern database systems.