Skip to content

PostgreSQL - Cookbook

A long list of things in the shortest possible way.

Data types

NameDescription
BIGINTSigned eight-byte integer
BIGSERIALAutoincrementing eight-byte integer
BOOLEAN / BOOLLogical Boolean (true/false)
VARCHARVariable-length character string
CIDRIPv4 or IPv6 network address
DATECalendar date (year, month, day)
DOUBLE PRECISIONDouble precision floating-point number (8 bytes)
INETIPv4 or IPv6 host address
INTEGER / INTSigned four-byte integer
INTERVALTime span
JSONTextual JSON data
MONEYCurrency amount
NUMERICExact numeric of selectable precision
REALSingle precision floating-point number (4 bytes)
SMALLINTSigned two-byte integer
SMALLSERIALAutoincrementing two-byte integer
SERIALAutoincrementing four-byte integer
TEXTVariable-length character string
TIMETime of day (no time zone)
TIMETZTime of day, including time zone
TIMESTAMPDate and time (no time zone)
TIMESTAMPTZDate and time, including time zone
XMLXML data

Documentation

Numeric types limits

Numeric types have hard limits which values are possible.

NameMinimumMaximum
SMALLINT / INT2-32 76832 767
INTEGER / INT-2,147 ∙ 1092,147 ∙ 109
BIGINT / INT8-9,223 ∙ 10189,223 ∙ 1018
SMALLSERIAL132 767
SERIAL12,147 ∙ 109
BIGSERIAL19,223 ∙ 1018

Documentation

Float numbers

Inexact options for precision numbers are REAL and DOUBLE PRECISION also known as FLOAT4 and FLOAT8 respectively. REAL variables are saved with 6 decimal digit and DOUBLE PRECISION with 15 decimal digit precision.

For more exact control of number precions NUMERIC should be used. The precision of a NUMERIC is the total count of digits in the whole number. The scale of a NUMERIC is the count of decimal digits.

sql
NUMERIC(precision, scale)
NUMERIC(precision, scale)
1

The NUMERIC datatype can save up to 131 072 digits before the decimal point or up to 16 383 digits after the decimal point.

Examples of numeric parameters:

NumberPrecisionScale
189.5652
23.514164
1.667223187
3142627.181
123.12363

Documentation

Date, time and timestamps

Limits for date and time variables.

NameMinimumMaximumResolution
TIMESTAMP4713 BC294276 AD1 microsecond
TIMESTAMPTZ4713 BC294276 AD1 microsecond
DATE4713 BC5874897 AD1 day
TIME00:00:0024:00:001 microsecond
TIMETZ00:00:00+155924:00:00-15591 microsecond
INTERVAL-178000000 years178000000 years1 microsecond

Documentation

Recommended formats:

sql
-- Date (YYYY-MM-DD)
2020-10-28

-- Time (HH:mm:ss)
01:23:06.789

-- Time with milliseconds (HH:mm:ss.[sss])
01:23:06.789

-- Timezone (abbreviation, time zone name or ISO-8601)
GMT
Europe/Helsinki
+2:00

-- Timestamp
2020-10-28 01:23:06.789

-- Timestamp with time zone
2020-10-28 01:23:06.789 +2:00
-- Date (YYYY-MM-DD)
2020-10-28

-- Time (HH:mm:ss)
01:23:06.789

-- Time with milliseconds (HH:mm:ss.[sss])
01:23:06.789

-- Timezone (abbreviation, time zone name or ISO-8601)
GMT
Europe/Helsinki
+2:00

-- Timestamp
2020-10-28 01:23:06.789

-- Timestamp with time zone
2020-10-28 01:23:06.789 +2:00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Enums

Enums are awesome for forcing set of values which Postgres don't have.

sql
CREATE TYPE USER_ROLE AS ENUM (
	'admin',
	'author',
	'editor',
	'guest'
);
CREATE TYPE USER_ROLE AS ENUM (
	'admin',
	'author',
	'editor',
	'guest'
);
1
2
3
4
5
6

Documentation

Creating tables

Create users table with id column as a primary key.

sql
CREATE TABLE users (
	-- auto incrementing id
	id SERIAL PRIMARY KEY,

	first_name VARCHAR(32) NOT NULL,
	last_name VARCHAR(32) NOT NULL,
	email VARCHAR(128) NOT NULL UNIQUE,
	password TEXT NOT NULL,
	is_activated BOOLEAN DEFAULT false,

	-- enum with default value set to 'guest'
	role USER_ROLE DEFAULT 'guest' NOT NULL,

	birthday DATE NOT NULL,
	time_of_death TIMESTAMPTZ,

	street_address VARCHAR(128),
	city VARCHAR(32),
	country VARCHAR(32),
	postal_code NUMERIC(5),

	height NUMERIC(5, 2) CHECK (height BETWEEN 40 AND 240),
	weight NUMERIC(5, 2) CHECK (weight > 1),
	shoe_size SMALLINT,

	-- default the creation time to NOW() value
	created_at TIMESTAMP DEFAULT NOW() NOT NULL,
	updated_at TIMESTAMP
);
CREATE TABLE users (
	-- auto incrementing id
	id SERIAL PRIMARY KEY,

	first_name VARCHAR(32) NOT NULL,
	last_name VARCHAR(32) NOT NULL,
	email VARCHAR(128) NOT NULL UNIQUE,
	password TEXT NOT NULL,
	is_activated BOOLEAN DEFAULT false,

	-- enum with default value set to 'guest'
	role USER_ROLE DEFAULT 'guest' NOT NULL,

	birthday DATE NOT NULL,
	time_of_death TIMESTAMPTZ,

	street_address VARCHAR(128),
	city VARCHAR(32),
	country VARCHAR(32),
	postal_code NUMERIC(5),

	height NUMERIC(5, 2) CHECK (height BETWEEN 40 AND 240),
	weight NUMERIC(5, 2) CHECK (weight > 1),
	shoe_size SMALLINT,

	-- default the creation time to NOW() value
	created_at TIMESTAMP DEFAULT NOW() NOT NULL,
	updated_at TIMESTAMP
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Documentation | Syntax

Relationships

Creating relations between tables are done with REFERENCES statement. Before adding rows to addresses and user_info tables referenced user must exist.

sql
CREATE TABLE users (
	id SERIAL PRIMARY KEY,
	first_name VARCHAR(32) NOT NULL,
);

CREATE TABLE addresses (
	id SERIAL PRIMARY KEY,
	street_address VARCHAR(128),

	-- Reference to users.id
	user_id INT REFERENCES users (id)
);

CREATE TABLE user_info (
	id SERIAL PRIMARY KEY,
	user_id INT,

	-- Reference to users.id with FOREIGN KEY constraint
	FOREIGN KEY (user_id)
		REFERENCES users (id)
);
CREATE TABLE users (
	id SERIAL PRIMARY KEY,
	first_name VARCHAR(32) NOT NULL,
);

CREATE TABLE addresses (
	id SERIAL PRIMARY KEY,
	street_address VARCHAR(128),

	-- Reference to users.id
	user_id INT REFERENCES users (id)
);

CREATE TABLE user_info (
	id SERIAL PRIMARY KEY,
	user_id INT,

	-- Reference to users.id with FOREIGN KEY constraint
	FOREIGN KEY (user_id)
		REFERENCES users (id)
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Many-to-many relations

In many-to-many relations one table works as a link between two. This way user_groups can have multiple permissions attached to it.

sql
CREATE TABLE permissions (
	name VARCHAR(128) PRIMARY KEY,
	description TEXT
);

CREATE TABLE user_groups (
	id SERIAL PRIMARY KEY,
	name VARCHAR(32) UNIQUE NOT NULL
);
CREATE TABLE permissions (
	name VARCHAR(128) PRIMARY KEY,
	description TEXT
);

CREATE TABLE user_groups (
	id SERIAL PRIMARY KEY,
	name VARCHAR(32) UNIQUE NOT NULL
);
1
2
3
4
5
6
7
8
9

The "middle" table references both tables above. With ON DELETE policies set when someone wants to remove a permission that is still referenced by an user_group_permissions table, it is not allowed. If someone removes an user group, all the group's permissions are removed as well from user_group_permissions table.

sql
CREATE TABLE user_group_permissions (
	permission_name VARCHAR(128)
		REFERENCES permissions (name) ON DELETE RESTRICT,

	group_id INTEGER
		REFERENCES user_groups (id) ON DELETE CASCADE,

	-- True if user has this power, false otherwise
	value BOOLEAN DEFAULT false NOT NULL,

	-- Multicolumn primary key
	PRIMARY KEY (permission_name, group_id)
);
CREATE TABLE user_group_permissions (
	permission_name VARCHAR(128)
		REFERENCES permissions (name) ON DELETE RESTRICT,

	group_id INTEGER
		REFERENCES user_groups (id) ON DELETE CASCADE,

	-- True if user has this power, false otherwise
	value BOOLEAN DEFAULT false NOT NULL,

	-- Multicolumn primary key
	PRIMARY KEY (permission_name, group_id)
);
1
2
3
4
5
6
7
8
9
10
11
12
13

On delete relationship

The ON DELETE clause specifies what action is performed when a referenced row in the referenced table is deleted.

ClauseDescription
NO ACTIONPrevents deletion of a referenced row. (Default)
RESTRICTPrevents deletion of a referenced row.
CASCADEReferenced rows should be automatically deleted.
SET NULLThe referencing column in referenced rows should be set nulls.
SET DEFAULTThe referencing column in referenced rows should be set default value.

Documentation

Editing tables

Rename the table.

sql
ALTER TABLE users RENAME TO app_users;
ALTER TABLE users RENAME TO app_users;
1

Add a new column called phone_number.

sql
ALTER TABLE users ADD COLUMN phone_number VARCHAR(32) NOT NULL;
ALTER TABLE users ADD COLUMN phone_number VARCHAR(32) NOT NULL;
1

Edit an existing column password to have different data type.

sql
ALTER TABLE users ALTER COLUMN password TYPE VARCHAR(512);
ALTER TABLE users ALTER COLUMN password TYPE VARCHAR(512);
1

Change role column's default value to "editor".

sql
ALTER TABLE users ALTER COLUMN role SET DEFAULT 'editor';
ALTER TABLE users ALTER COLUMN role SET DEFAULT 'editor';
1

Rename column street_address to address.

sql
ALTER TABLE users RENAME COLUMN street_address TO address;
ALTER TABLE users RENAME COLUMN street_address TO address;
1

Drop a column shoe_size.

sql
ALTER TABLE users DROP COLUMN shoe_size;
ALTER TABLE users DROP COLUMN shoe_size;
1

Documentation

Deleting tables

Deleting table is simple and dangerous. Make backups first!

sql
DROP TABLE [IF EXISTS] users;
DROP TABLE [IF EXISTS] users;
1

Documentation

Constraints

Constraints can be added to columns while creating table or added to tables after creation.

sql
ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email);
ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email);
1
2

Documentation

Check constraints

A check constraint allows to specify the value in a certain column that must satisfy a Boolean expression.

sql
[CONSTRAINT check_constraint_name] CHECK (expression);
[CONSTRAINT check_constraint_name] CHECK (expression);
1

Example of forcing height value to be between 40 and 240 and weight larger than 1.

sql
CREATE TABLE users (
	height NUMERIC(5, 2) CHECK (height BETWEEN 40 AND 240),
	weight NUMERIC(5, 2) CHECK (weight > 1)
);
CREATE TABLE users (
	height NUMERIC(5, 2) CHECK (height BETWEEN 40 AND 240),
	weight NUMERIC(5, 2) CHECK (weight > 1)
);
1
2
3
4

A check constraint can also refer to several columns. The following example forces that shoe_size must be smaller than height.

sql
CREATE TABLE users (
	shoe_size SMALLINT,
	CHECK (shoe_size > 0),

	height NUMERIC(5, 2)
	CHECK (height BETWEEN 40 AND 240),

	CHECK (shoe_size < height)
);
CREATE TABLE users (
	shoe_size SMALLINT,
	CHECK (shoe_size > 0),

	height NUMERIC(5, 2)
	CHECK (height BETWEEN 40 AND 240),

	CHECK (shoe_size < height)
);
1
2
3
4
5
6
7
8
9

Documentation

Unique constraints

Unique constraints ensure that the data contained in a column, or a group of columns, is unique among all the rows in the table.

sql
[CONSTRAINT unique_constraint_name] UNIQUE (column1, [ column2 ])
[CONSTRAINT unique_constraint_name] UNIQUE (column1, [ column2 ])
1

Ensure that email field is unique for each user.

sql
CREATE TABLE users (
	email VARCHAR(128) NOT NULL UNIQUE
);
CREATE TABLE users (
	email VARCHAR(128) NOT NULL UNIQUE
);
1
2
3

Ensure that first_name and last_name combo of is unique by setting group of columns as unique.

sql
CREATE TABLE users (
	first_name VARCHAR(32) NOT NULL,
	last_name VARCHAR(32) NOT NULL,
	UNIQUE (first_name, last_name)
);
CREATE TABLE users (
	first_name VARCHAR(32) NOT NULL,
	last_name VARCHAR(32) NOT NULL,
	UNIQUE (first_name, last_name)
);
1
2
3
4
5

PostgreSQL automatically creates an unique index when an unique constraint or primary key is defined for a table.

Documentation

Primary keys

Primary key requires that the value is both unique and not null. Table's primary key may reference to multiple columns.

sql
[CONSTRAINT pk_constraint_name] PRIMARY KEY (column1, [ column2 ])
[CONSTRAINT pk_constraint_name] PRIMARY KEY (column1, [ column2 ])
1

Documentation

Indexes

Indexes speed up queries to the table when indexed columns are used in a statements. Use following command to create index on column_name column:

sql
CREATE INDEX index_name ON table_name (column_name);
CREATE INDEX index_name ON table_name (column_name);
1

To remove an index, use the DROP INDEX command.

Documentation

Manipulating data

Inserting data

Inserting data to table is done with INSERT INTO statement. Column names can be omitted if inserting in all column in right order.

sql
INSERT INTO table_name (col1, col2)
VALUES (value1, value2),
	(value3, value4);
INSERT INTO table_name (col1, col2)
VALUES (value1, value2),
	(value3, value4);
1
2
3

Insert four rows into users table (rest of the columns are omitted for readability).

sql
INSERT INTO users (first_name, last_name, height, user_role)
VALUES ('Jane', 'Doe', 193.95, 'admin'),
	('Mick', 'Pot', 175.20, DEFAULT),
	('Tom', 'Smith', 186.45, DEFAULT);
INSERT INTO users (first_name, last_name, height, user_role)
VALUES ('Jane', 'Doe', 193.95, 'admin'),
	('Mick', 'Pot', 175.20, DEFAULT),
	('Tom', 'Smith', 186.45, DEFAULT);
1
2
3
4

Documentation

Editing data

Data in the table is updated with UPDATE statement.

sql
UPDATE table_name
SET column = new_value
[WHERE condition]
UPDATE table_name
SET column = new_value
[WHERE condition]
1
2
3

Edit users which have id equal to 5, 8 or 13.

sql
UPDATE users
SET user_role = 'guest', first_name = 'Mike'
WHERE id IN (5, 8, 13);
UPDATE users
SET user_role = 'guest', first_name = 'Mike'
WHERE id IN (5, 8, 13);
1
2
3

Documentation

Removing data

Rows can be deleted with DELETE statement.

sql
DELETE FROM table_name
[WHERE condition]
DELETE FROM table_name
[WHERE condition]
1
2

Delete rows which have last_name equal to "Smith".

sql
DELETE FROM users
WHERE last_name = 'Smith';
DELETE FROM users
WHERE last_name = 'Smith';
1
2

Delete all rows from table aka truncate.

sql
DELETE FROM table_name;
DELETE FROM table_name;
1

Documentation

Querying data

Select columns from tables by comma-separated list. All columns can be selected with *.

sql
SELECT column1, column2
FROM table_name;
SELECT column1, column2
FROM table_name;
1
2

Documentation

Functions

See list of all functions.

Math

Returns a random value in the range 0.0 <= x < 1.0.

sql
RANDOM() → DOUBLE PRECISION
RANDOM() → DOUBLE PRECISION
1

Rounds value to amount of digits.

sql
ROUND( value NUMERIC, amount INTEGER ) → NUMERIC
ROUND( value NUMERIC, amount INTEGER ) → NUMERIC
1

Approximate value of π.

sql
PI() → DOUBLE PRECISION
PI() → DOUBLE PRECISION
1

All math functions

String

Converts the string to lower case.

sql
LOWER( string TEXT ) → TEXT
LOWER( string TEXT ) → TEXT
1

Converts the string to upper case.

sql
UPPER( string TEXT ) → TEXT
UPPER( string TEXT ) → TEXT
1

Extracts the substring of string starting at the start character and stopping after count of characters.

sql
SUBSTRING( string TEXT [ FROM start INTEGER ] [ FOR count INTEGER ] ) → TEXT
SUBSTRING( string TEXT [ FROM start INTEGER ] [ FOR count INTEGER ] ) → TEXT
1

Formats arguments according to a format string. Similar to the C's sprintf see here.

sql
FORMAT( format TEXT [ args ] ) → TEXT
FORMAT( format TEXT [ args ] ) → TEXT
1

Returns first n characters of the string.

sql
LEFT( string TEXT, n INTEGER ) → TEXT
LEFT( string TEXT, n INTEGER ) → TEXT
1

Returns last n characters of the string.

sql
RIGHT( string TEXT, n INTEGER ) → TEXT
RIGHT( string TEXT, n INTEGER ) → TEXT
1

Returns the number of characters in the string.

sql
LENGTH( string TEXT ) → INTEGER
LENGTH( string TEXT ) → INTEGER
1

All string functions

Aggregate

Computes the average of all the non-null input values.

sql
AVG( any number ) → NUMERIC
AVG( any number ) → NUMERIC
1

Computes the number of input rows.

sql
COUNT( * ) → BIGINT
COUNT( * ) → BIGINT
1

Computes the maximum of the non-null input values.

sql
MAX( any value ) → any value
MAX( any value ) → any value
1

Computes the minimum of the non-null input values.

sql
MIN( any value ) → any value
MIN( any value ) → any value
1

Computes the sum of the non-null input values.

sql
SUM( any number ) → NUMERIC
SUM( any number ) → NUMERIC
1

Computes the median value of the non-null input values.

sql
PERCENTILE_CONT( 0.5 ) WITHIN GROUP ( ORDER BY any number ) → DOUBLE PRECISION
PERCENTILE_CONT( 0.5 ) WITHIN GROUP ( ORDER BY any number ) → DOUBLE PRECISION
1

All aggregate functions

Date and time

With date and time data types basic mathematical operations are available.

Get timestamp subfield, more info here.

sql
DATE_PART( field STRING, timestamp ) → DOUBLE PRECISION
EXTRACT( field FROM timestamp ) → DOUBLE PRECISION
DATE_PART( field STRING, timestamp ) → DOUBLE PRECISION
EXTRACT( field FROM timestamp ) → DOUBLE PRECISION
1
2

Truncate to specified precision, more info here.

sql
DATE_TRUNC( field STRING, TIMESTAMP ) → TIMESTAMP
DATE_TRUNC( field STRING, TIMESTAMP ) → TIMESTAMP
1

Convert Unix epoch to timestamp with time zone.

sql
TO_TIMESTAMP( DOUBLE PRECISION ) → TIMESTAMPTZ
TO_TIMESTAMP( DOUBLE PRECISION ) → TIMESTAMPTZ
1

All date and time functions

Distinct

Duplicate rows can be eliminated with DISTINCT key word after SELECT.

sql
SELECT DISTINCT column1, column2 FROM table_name;
SELECT DISTINCT column1, column2 FROM table_name;
1

Documentation

Sorting

Rows can sorted with ORDER BY clause in ascending (default) or descending order.

sql
ORDER BY expression [ASC | DESC] [NULLS FIRST | NULLS LAST]
ORDER BY expression [ASC | DESC] [NULLS FIRST | NULLS LAST]
1

Sort first by birthday and then by first_name value without overriding first order. The 2nd order by values are sorted inside previous sorted values.

sql
SELECT first_name, last_name, birthday
FROM users
ORDER BY birthday ASC, first_name DESC;
SELECT first_name, last_name, birthday
FROM users
ORDER BY birthday ASC, first_name DESC;
1
2
3

Order rows with length of a first_name column.

sql
SELECT first_name, LENGTH(first_name) AS length
FROM customer
ORDER BY length DESC;
SELECT first_name, LENGTH(first_name) AS length
FROM customer
ORDER BY length DESC;
1
2
3

Documentation

Limiting and offsetting

Returned rows can be limited and skipped with LIMIT AND OFFSET.

sql
LIMIT row_count OFFSET rows_to_skip;
LIMIT row_count OFFSET rows_to_skip;
1

Get 10 row from users table skipping first 20 of them.

sql
SELECT first_name, last_name
FROM users
LIMIT 10 OFFSET 20;
SELECT first_name, last_name
FROM users
LIMIT 10 OFFSET 20;
1
2
3

Documentation

Filtering

Basic arithmetical operators (=, <= etc.) are supported. Other available operators are:

OperatorDescription
<> or !=Not equal
ANDLogical operator AND
ORLogical operator OR
INValue matches any value in a list
BETWEENValue is between a range of values
LIKEValue matches a pattern (case sensitive)
ILIKEValue matches a pattern (case insensitive)
IS NULLValue is NULL
NOTNegate the result of other operators
~Regular expression

Filter returned rows with WHERE clause.

sql
WHERE conditions;
WHERE conditions;
1

Get users which height is between 175 and 185 and shoe_size is not NULL.

sql
SELECT first_name, last_name
FROM users
WHERE height BETWEEN 175 AND 185
	AND shoe_size IS NOT NULL;
SELECT first_name, last_name
FROM users
WHERE height BETWEEN 175 AND 185
	AND shoe_size IS NOT NULL;
1
2
3
4

Documentation

Pattern Matching

Match patterns with LIKE operator. An underscore _ in pattern stands for any single character and a percent sign % matches any sequence of zero or more characters.

Get users which last_name includes "mac" or first_name doesn't end with "n".

sql
SELECT first_name, last_name
FROM users
WHERE last_name ILIKE '%mac%'
	OR first_name NOT LIKE '%n';
SELECT first_name, last_name
FROM users
WHERE last_name ILIKE '%mac%'
	OR first_name NOT LIKE '%n';
1
2
3
4

Regular Expressions

Get users which last_name includes "mac" or first_name doesn't end with "n" with regular expression.

asdasdasds

sql
SELECT first_name, last_name
FROM users
WHERE last_name ~ '.*mac.*'
	OR first_name NOT LIKE '.*n$​';
SELECT first_name, last_name
FROM users
WHERE last_name ~ '.*mac.*'
	OR first_name NOT LIKE '.*n$​';
1
2
3
4

Full list of regex escapes and constraints can be found in documentation.

Grouping

Rows can be grouped together with GROUP BY clause to produce different types of aggregates such as sums, averages and counts.

sql
GROUP BY column_name;
GROUP BY column_name;
1

Get min, max and average shoe sizes for every country from users which were born after year 1999.

sql
SELECT country,
	MIN(shoe_size) AS minimum_size,
	MAX(shoe_size) AS maximum_size,
	AVG(shoe_size) AS average_size
FROM users
WHERE birthday >= '2000-01-01'
GROUP BY country;
SELECT country,
	MIN(shoe_size) AS minimum_size,
	MAX(shoe_size) AS maximum_size,
	AVG(shoe_size) AS average_size
FROM users
WHERE birthday >= '2000-01-01'
GROUP BY country;
1
2
3
4
5
6
7

Documentation

Group by having

If a table has been grouped using GROUP BY, but only certain groups are of interest, the HAVING clause can be used to eliminate groups from the result.

sql
GROUP BY column_name HAVING expression;
GROUP BY column_name HAVING expression;
1

Get average weight of users grouped by country and having the average over 100.

sql
SELECT country,
	AVG(weight) AS weight
FROM users
WHERE birthday >= '2000-01-01'
GROUP BY country
	HAVING AVG(weight) > 100;
SELECT country,
	AVG(weight) AS weight
FROM users
WHERE birthday >= '2000-01-01'
GROUP BY country
	HAVING AVG(weight) > 100;
1
2
3
4
5
6

Limitations of grouping

Grouping rows with non-aggregated or grouped columns are not allowed. In example below it's impossible to get first_names of users because they are grouped together by shoe_size.

sql
SELECT shoe_size,
	AVG(weight) AS average_weight,
	first_name -- ERROR: column "users.first_name" must appear in the GROUP BY clause or used in an aggregate function
FROM users
WHERE birthday <= '2000-01-01'
GROUP BY shoe_size;
SELECT shoe_size,
	AVG(weight) AS average_weight,
	first_name -- ERROR: column "users.first_name" must appear in the GROUP BY clause or used in an aggregate function
FROM users
WHERE birthday <= '2000-01-01'
GROUP BY shoe_size;
1
2
3
4
5
6

All rows in a users table.

IDFirst nameShoe sizeWeight
1Jane3967
2Tom3975
3Pete3974
4Mick4288

With non-aggregated column in grouped SELECT clause generates problem for PostgreSQL deciding which value of three first_names to pick.

First nameShoe sizeAverage Weight
Jane or Tom or Pete?3975
Mick4288

Joining tables

Queries can access multiple tables at once with syntax below:

sql
[LEFT | RIGHT | CROSS | INNER | FULL | SELF] JOIN table_name condition
[LEFT | RIGHT | CROSS | INNER | FULL | SELF] JOIN table_name condition
1

SQL Join types

Get all users with their cities from addresses table with JOIN.

sql
SELECT users.first_name, users.last_name, addresses.city
FROM users
JOIN addresses
	ON users.id = addresses.user_id;
SELECT users.first_name, users.last_name, addresses.city
FROM users
JOIN addresses
	ON users.id = addresses.user_id;
1
2
3
4

Documentation

Many-to-many join

Get user groups and permissions that groups have.

sql
SELECT permissions.name AS permission, user_groups.name AS group, ugp.value
	FROM permissions
JOIN user_group_permissions AS ugp
	ON permissions.name = ugp.permission_name
JOIN user_groups
	ON ugp.group_id = user_groups.id
WHERE ugp.value = true;
SELECT permissions.name AS permission, user_groups.name AS group, ugp.value
	FROM permissions
JOIN user_group_permissions AS ugp
	ON permissions.name = ugp.permission_name
JOIN user_groups
	ON ugp.group_id = user_groups.id
WHERE ugp.value = true;
1
2
3
4
5
6
7

Links