Skip to main content

PostgreSQL 14 Database Refresher :: Introduction Part I

Shenzhen, China

PostgreSQL is a powerful, open source object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.

Setup

I am going to use the official Docker image to set up the SQL Database on a Debian Bullseye server:

docker run -d --rm \
--name postgres \
-e POSTGRES_PASSWORD=secretpassword \
-p 5432:5432 \
postgres:14

I can now access the container and connect to the Postgres CLI:

docker exec -ti -u postgres postgres psql

psql (14.0 (Debian 14.0-1.pgdg110+1))
Type "help" for help.

postgres=#

Overview

List of all Available Commands

\?

General
\copyright show PostgreSQL usage and distribution terms
\crosstabview [COLUMNS] execute query and display results in crosstab
\errverbose show most recent error message at maximum verbosity
\g [(OPTIONS)] [FILE] execute query (and send results to file or |pipe);
\g with no arguments is equivalent to a semicolon
\gdesc describe result of query, without executing it
\gexec execute query, then execute each value in its result
\gset [PREFIX] execute query and store results in psql variables
\gx [(OPTIONS)] [FILE] as \g, but forces expanded output mode
\q quit psql
\watch [SEC] execute query every SEC seconds

Help
\? [commands] show help on backslash commands
\? options show help on psql command-line options
\? variables show help on special variables
\h [NAME] help on syntax of SQL commands, * for all commands

Query Buffer
\e [FILE] [LINE] edit the query buffer (or file) with external editor
\ef [FUNCNAME [LINE]] edit function definition with external editor
\ev [VIEWNAME [LINE]] edit view definition with external editor
\p show the contents of the query buffer
\r reset (clear) the query buffer
\s [FILE] display history or save it to file
\w FILE write query buffer to file

Input/Output
\copy ... perform SQL COPY with data stream to the client host
\echo [-n] [STRING] write string to standard output (-n for no newline)
\i FILE execute commands from file
\ir FILE as \i, but relative to location of current script
\o [FILE] send all query results to file or |pipe
\qecho [-n] [STRING] write string to \o output stream (-n for no newline)
\warn [-n] [STRING] write string to standard error (-n for no newline)

Conditional
\if EXPR begin conditional block
\elif EXPR alternative within current conditional block
\else final alternative within current conditional block
\endif end conditional block

Informational
(options: S = show system objects, + = additional detail)
\d[S+] list tables, views, and sequences
\d[S+] NAME describe table, view, sequence, or index
\da[S] [PATTERN] list aggregates
\dA[+] [PATTERN] list access methods
\dAc[+] [AMPTRN [TYPEPTRN]] list operator classes
Most commands optionally preceded by integer argument k. Defaults in brackets.
Star (*) indicates argument becomes new default.
-------------------------------------------------------------------------------
<space> Display next k lines of text [current screen size]
z Display next k lines of text [current screen size]*
<return> Display next k lines of text [1]*
d or ctrl-D Scroll k lines [current scroll size, initially 11]*
q or Q or <interrupt> Exit from more
s Skip forward k lines of text [1]
f Skip forward k screenfuls of text [1]
b or ctrl-B Skip backwards k screenfuls of text [1]
' Go to place where previous search started
= Display current line number
/<regular expression> Search for kth occurrence of regular expression [1]
n Search for kth occurrence of last r.e [1]
!<cmd> or :!<cmd> Execute <cmd> in a subshell
v Start up '/usr/bin/vi' at current line
ctrl-L Redraw screen
:n Go to kth next file [1]
:p Go to kth previous file [1]
:f Display current file name and line number
. Repeat previous command
-------------------------------------------------------------------------------

...skipping 1 line
\dAo[+] [AMPTRN [OPFPTRN]] list operators of operator families
\dAp[+] [AMPTRN [OPFPTRN]] list support functions of operator families
\db[+] [PATTERN] list tablespaces
\dc[S+] [PATTERN] list conversions
\dC[+] [PATTERN] list casts
\dd[S] [PATTERN] show object descriptions not displayed elsewhere
\dD[S+] [PATTERN] list domains
\ddp [PATTERN] list default privileges
\dE[S+] [PATTERN] list foreign tables
\des[+] [PATTERN] list foreign servers
\det[+] [PATTERN] list foreign tables
\deu[+] [PATTERN] list user mappings
\dew[+] [PATTERN] list foreign-data wrappers
\df[anptw][S+] [FUNCPTRN [TYPEPTRN ...]]
list [only agg/normal/procedure/trigger/window] functions
\dF[+] [PATTERN] list text search configurations
\dFd[+] [PATTERN] list text search dictionaries
\dFp[+] [PATTERN] list text search parsers
\dFt[+] [PATTERN] list text search templates
\dg[S+] [PATTERN] list roles
\di[S+] [PATTERN] list indexes
\dl list large objects, same as \lo_list
\dL[S+] [PATTERN] list procedural languages
\dm[S+] [PATTERN] list materialized views
\dn[S+] [PATTERN] list schemas
\do[S+] [OPPTRN [TYPEPTRN [TYPEPTRN]]]
list operators
\dO[S+] [PATTERN] list collations
\dp [PATTERN] list table, view, and sequence access privileges
\dP[itn+] [PATTERN] list [only index/table] partitioned relations [n=nested]
\drds [ROLEPTRN [DBPTRN]] list per-database role settings
\dRp[+] [PATTERN] list replication publications
\dRs[+] [PATTERN] list replication subscriptions
\ds[S+] [PATTERN] list sequences
\dt[S+] [PATTERN] list tables
\dT[S+] [PATTERN] list data types
\du[S+] [PATTERN] list roles
\dv[S+] [PATTERN] list views
\dx[+] [PATTERN] list extensions
\dX [PATTERN] list extended statistics
\dy[+] [PATTERN] list event triggers
\l[+] [PATTERN] list databases
\sf[+] FUNCNAME show a function's definition
\sv[+] VIEWNAME show a view's definition
\z [PATTERN] same as \dp

Formatting
\a toggle between unaligned and aligned output mode
\C [STRING] set table title, or unset if none
\f [STRING] show or set field separator for unaligned query output

...skipping 1 line
\pset [NAME [VALUE]] set table output option
(border|columns|csv_fieldsep|expanded|fieldsep|
fieldsep_zero|footer|format|linestyle|null|
numericlocale|pager|pager_min_lines|recordsep|
recordsep_zero|tableattr|title|tuples_only|
unicode_border_linestyle|unicode_column_linestyle|
unicode_header_linestyle)
\t [on|off] show only rows (currently off)
\T [STRING] set HTML <table> tag attributes, or unset if none
\x [on|off|auto] toggle expanded output (currently off)

Connection
\c[onnect] {[DBNAME|- USER|- HOST|- PORT|-] | conninfo}
connect to new database (currently "message_boards")
\conninfo display information about current connection
\encoding [ENCODING] show or set client encoding
\password [USERNAME] securely change the password for a user

Operating System
\cd [DIR] change the current working directory
\setenv NAME [VALUE] set or unset environment variable
\timing [on|off] toggle timing of commands (currently off)
\! [COMMAND] execute command in shell or start interactive shell

Variables
\prompt [TEXT] NAME prompt user to set internal variable
\set [NAME [VALUE]] set internal variable, or list all if no parameters
\unset NAME unset (delete) internal variable

Large Objects
\lo_export LOBOID FILE
\lo_import FILE [COMMENT]
\lo_list
\lo_unlink LOBOID large object operations

List of all Available Queries

\h
Available help:
ABORT CHECKPOINT CREATE USER DROP TRIGGER
ALTER AGGREGATE CLOSE CREATE USER MAPPING DROP TYPE
ALTER COLLATION CLUSTER CREATE VIEW DROP USER
ALTER CONVERSION COMMENT DEALLOCATE DROP USER MAPPING
ALTER DATABASE COMMIT DECLARE DROP VIEW
ALTER DEFAULT PRIVILEGES COMMIT PREPARED DELETE END
ALTER DOMAIN COPY DISCARD EXECUTE
ALTER EVENT TRIGGER CREATE ACCESS METHOD DO EXPLAIN
ALTER EXTENSION CREATE AGGREGATE DROP ACCESS METHOD FETCH
ALTER FOREIGN DATA WRAPPER CREATE CAST DROP AGGREGATE GRANT
ALTER FOREIGN TABLE CREATE COLLATION DROP CAST IMPORT FOREIGN SCHEMA
ALTER FUNCTION CREATE CONVERSION DROP COLLATION INSERT
ALTER GROUP CREATE DATABASE DROP CONVERSION LISTEN
ALTER INDEX CREATE DOMAIN DROP DATABASE LOAD
ALTER LANGUAGE CREATE EVENT TRIGGER DROP DOMAIN LOCK
ALTER LARGE OBJECT CREATE EXTENSION DROP EVENT TRIGGER MOVE
ALTER MATERIALIZED VIEW CREATE FOREIGN DATA WRAPPER DROP EXTENSION NOTIFY
ALTER OPERATOR CREATE FOREIGN TABLE DROP FOREIGN DATA WRAPPER PREPARE
ALTER OPERATOR CLASS CREATE FUNCTION DROP FOREIGN TABLE PREPARE TRANSACTION
ALTER OPERATOR FAMILY CREATE GROUP DROP FUNCTION REASSIGN OWNED
ALTER POLICY CREATE INDEX DROP GROUP REFRESH MATERIALIZED VIEW
ALTER PROCEDURE CREATE LANGUAGE DROP INDEX REINDEX
ALTER PUBLICATION CREATE MATERIALIZED VIEW DROP LANGUAGE RELEASE SAVEPOINT
ALTER ROLE CREATE OPERATOR DROP MATERIALIZED VIEW RESET
ALTER ROUTINE CREATE OPERATOR CLASS DROP OPERATOR REVOKE
ALTER RULE CREATE OPERATOR FAMILY DROP OPERATOR CLASS ROLLBACK
ALTER SCHEMA CREATE POLICY DROP OPERATOR FAMILY ROLLBACK PREPARED
ALTER SEQUENCE CREATE PROCEDURE DROP OWNED ROLLBACK TO SAVEPOINT
ALTER SERVER CREATE PUBLICATION DROP POLICY SAVEPOINT
ALTER STATISTICS CREATE ROLE DROP PROCEDURE SECURITY LABEL
ALTER SUBSCRIPTION CREATE RULE DROP PUBLICATION SELECT
ALTER SYSTEM CREATE SCHEMA DROP ROLE SELECT INTO
ALTER TABLE CREATE SEQUENCE DROP ROUTINE SET
ALTER TABLESPACE CREATE SERVER DROP RULE SET CONSTRAINTS
ALTER TEXT SEARCH CONFIGURATION CREATE STATISTICS DROP SCHEMA SET ROLE
ALTER TEXT SEARCH DICTIONARY CREATE SUBSCRIPTION DROP SEQUENCE SET SESSION AUTHORIZATION
ALTER TEXT SEARCH PARSER CREATE TABLE DROP SERVER SET TRANSACTION
ALTER TEXT SEARCH TEMPLATE CREATE TABLE AS DROP STATISTICS SHOW
ALTER TRIGGER CREATE TABLESPACE DROP SUBSCRIPTION START TRANSACTION
ALTER TYPE CREATE TEXT SEARCH CONFIGURATION DROP TABLE TABLE
ALTER USER CREATE TEXT SEARCH DICTIONARY DROP TABLESPACE TRUNCATE
ALTER USER MAPPING CREATE TEXT SEARCH PARSER DROP TEXT SEARCH CONFIGURATION UNLISTEN
ALTER VIEW CREATE TEXT SEARCH TEMPLATE DROP TEXT SEARCH DICTIONARY UPDATE
ANALYZE CREATE TRANSFORM DROP TEXT SEARCH PARSER VACUUM
BEGIN CREATE TRIGGER DROP TEXT SEARCH TEMPLATE VALUES
CALL CREATE TYPE DROP TRANSFORM WITH

Create a Database for your Application

Create the Database

CREATE DATABASE

CREATE DATABASE message_boards;

Switch to using the new message_boards table instead of the default postgres:

\connect message_boards;
You are now connected to database "message_boards" as user "postgres".

List all databases:

\list
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
----------------+----------+----------+------------+------------+-----------------------
message_boards | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
(4 rows)

Create the Tables

CREATE TABLES

CREATE TABLE users (
user_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
username VARCHAR ( 25 ) UNIQUE NOT NULL,
email VARCHAR ( 50 ) UNIQUE NOT NULL,
full_name VARCHAR ( 100 ) NOT NULL,
last_login TIMESTAMP,
created_on TIMESTAMP NOT NULL
);

CREATE TABLE boards (
board_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
board_name VARCHAR ( 50 ) UNIQUE NOT NULL,
board_description TEXT NOT NULL
);

CREATE TABLE comments (
comment_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id INT REFERENCES users(user_id) ON DELETE CASCADE,
board_id INT REFERENCES boards(board_id) ON DELETE CASCADE,
comment TEXT NOT NULL,
time TIMESTAMP
);

CREATE TABLE rich_content (
content_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
comment_id INT REFERENCES comments(comment_id) ON DELETE CASCADE,
content JSONB NOT NULL
);

List all existing tables with:

\d
List of relations
Schema | Name | Type | Owner
--------+-----------------------------+----------+----------
public | boards | table | postgres
public | boards_board_id_seq | sequence | postgres
public | comments | table | postgres
public | comments_comment_id_seq | sequence | postgres
public | rich_content | table | postgres
public | rich_content_content_id_seq | sequence | postgres
public | users | table | postgres
public | users_user_id_seq | sequence | postgres
(8 rows)

Insert Data

INSERT INTO

INSERT INTO users (username, email, full_name, created_on) VALUES ('mpolinowski', 'm.polinowski@myemail.com', 'Mike Polinowski', NOW());

Read Data

SELECT All

SELECT * FROM users;

user_id | username | email | full_name | last_login | created_on
---------+-------------+--------------------------+-----------------+------------+----------------------------
1 | mpolinowski | m.polinowski@myemail.com | Mike Polinowski | | 2021-10-09 08:20:30.088139
(1 row)

SELECT Specific

SELECT full_name FROM users;

full_name
-----------------
Mike Polinowski
(1 row)

SELECT WHERE

Find email address of a specific user:

SELECT email FROM users WHERE username='mpolinowski';

email
--------------------------
m.polinowski@myemail.com
(1 row)

Find all users that never logged in:

SELECT * FROM users WHERE last_login IS NULL;

user_id | username | email | full_name | last_login | created_on
---------+-------------+--------------------------+-----------------+------------+----------------------------
1 | mpolinowski | m.polinowski@myemail.com | Mike Polinowski | | 2021-10-09 08:20:30.088139
(1 row)

Find all users that never logged in and who's sign-up is longer than 6 months ago:

SELECT * FROM users WHERE last_login IS NULL AND created_on < NOW() - interval '6 months';

Find newest / oldest user account:

SELECT * FROM users ORDER BY created_on DESC LIMIT 1;
SELECT * FROM users ORDER BY created_on LIMIT 1;

Count number of users and number of users that actually logged in:

select COUNT(*) FROM users;
select COUNT(last_login) FROM users;

LIMIT

Limit the amount of returned data:

SELECT * FROM users LIMIT 5;

UPDATE

Tag a user that just logged in:

UPDATE username, last_login SET last_login=NOW() WHERE username='mpolinowski' RETURNING *;

username | last_login
-------------+----------------------------
mpolinowski | 2021-10-09 09:24:05.623879
(1 row)
UPDATE 1

Delete Data

DELETE FROM

DELETE FROM users WHERE username='mpolinowski';

Drop Entire Tables

DROP TABLE

DROP TABLE IF EXISTS rich_content;
DROP TABLE IF EXISTS comments;
DROP TABLE IF EXISTS boards;
DROP TABLE IF EXISTS users;