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;