PostgreSQL Basics
This is a series of notes from FreeCodeCamp’s PostgreSQL tutorial. The youtube video can be found here.
- Install
- Creating databases
- List Tables in DB
- List Columns in Table
- Creating Tables Part 2 – With Constraints
- Dropping tables
- Insert
- View Table Contents
- Mockaroo
- Select
- Limit
- Order By
- Distinct
- Where
- AND - OR
- Comparison Operators
- Offset
- Fetch
- IN
- Between
- Like
- iLike
- Group By
- Having
- Exporting Query Results
- Max, Min, Avg
- Operations
- Operations on Data
- Alias
- Coalesce
- NullIf
- Now
- Interval
- Extract
- Age
- Prefix
- Primary Key
- Constrains
- On Conflict
- Delete
- Update
- Upsert
- Foreign Key
- Joins
Install
On Arch Linux install with pacman
.
pacman -Ss postgresql
Then start with systemctl
systemctl start postgresql
Login to CLI
Access the PostgreSQL CLI via:
sudo -u postgres psql
Creating databases
Within the psql CLI you can run
CREATE DATABASE db_name;
To create a new database.
All commands must end with a
;
!!
postgres=# CREATE DATABASE test;
CREATE DATABASE
postgres=#
Connect to the DB
psql -U username db_name
Or, from within the psql cli
\c db_name
Dropping Databases
Within the psql cli: DROP DATABASE db_name
Creating Tables
CREATE TABLE table_name (
column_name data_type constraints[optional]
)
Person Example:
CREATE TABLE person (
id INT,
first_name VARCHAR(50),
last_name VARCHAR(50),
gender VARCHAR(6),
date_of_birth DATE
);
List Tables in DB
postTesting=# \d
List of relations
Schema | Name | Type | Owner
--------+--------+-------+-------
public | person | table | kgb33
(1 row)
List Columns in Table
postTesting=# \d person
Table "public.person"
Column | Type | Collation | Nullable | Default
---------------+-----------------------+-----------+----------+---------
id | integer | | |
first_name | character varying(50) | | |
last_name | character varying(50) | | |
gender | character varying(6) | | |
date_of_birth | date | | |
Creating Tables Part 2 – With Constraints
CREATE TABLE person (
id BIGSERIAL NOT NULL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
gender VARCHAR(6) NOT NULL,
date_of_birth DATE NOT NULL
);
NOT NULL
- Value cannot be NULLPRIMARY KEY
-BIGSERIAL
- Signed Int, Auto increments
postTesting=# \d
List of relations
Schema | Name | Type | Owner
--------+---------------+----------+-------
public | person | table | kgb33
public | person_id_seq | sequence | kgb33
(2 rows)
postTesting=# \d person
Table "public.person"
Column | Type | Collation | Nullable | Default
---------------+-----------------------+-----------+----------+------------------------------------
id | bigint | | not null | nextval('person_id_seq'::regclass)
first_name | character varying(50) | | not null |
last_name | character varying(50) | | not null |
gender | character varying(6) | | not null |
date_of_birth | date | | not null |
Indexes:
"person_pkey" PRIMARY KEY, btree (id)
Dropping tables
DROP TABLE table_name;
Insert
INSERT INTO table_name (
columns
...
...)
VALUES (var, ..., ...);
Example:
INSERT INTO person (
first_name,
last_name,
gender,
date_of_birth)
VALUES ('Anne', 'Smith', 'FEMALE', DATE '1988-01-09');
INSERT 0 1
- DATE format is YEAR-MONTH-DAY
- id is not specified because PostgreSQL auto increments it for us
View Table Contents
Use SQL SELECT * FROM table_name
to view all entries.
SELECT * FROM person;
id | first_name | last_name | gender | date_of_birth
----+------------+-----------+--------+---------------
1 | jake | jones | MALE | 1990-01-10
2 | Anne | Smith | FEMALE | 1988-01-09
(2 rows)
Mockaroo
Generating 1,000 rows of data using Mockaroo
After downloading changed the following in the CREATE TABLE command
- Added NOT NULL constrains
- Added id and corresponding info
Generated data is in person.sql
Within psql run \i ./persons.sql
to run all the SQL code within the file
Select
SELECT $column_name FROM $table_name;
*
is the wild card operator, i.e.
SELECT * FROM $table_name;
will select all columns.
LIMIT int
will limit the returned rows to the specified integer
To select multiple columns separate the column names with commas.
SELECT column_1, column_2, ..., column_n FROM table_name
Limit
LIMIT int
will limit the returned rows to the specified integer
SELECT * FROM $table_name LIMIT 10
Will return the first 10 rows
Selecting one column:
SELECT first_name FROM person LIMIT 5;
first_name
------------
Sargent
Tobias
Wait
Lulita
Tobye
(5 rows)
Selecting multiple Columns:
SELECT first_name, last_name FROM person LIMIT 5;
first_name | last_name
------------+-----------
Sargent | Matusiak
Tobias | Bulled
Wait | Deeves
Lulita | Olivo
Tobye | Blaisdell
(5 rows)
Order By
Order by has two modes, ASC and DESC. Ascending is default
SELECT $column_A FROM $table_name ORDER BY $column_B
Default ASC mode
SELECT * FROM person ORDER BY contry_of_birth LIMIT 10;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
-----+-------------+-----------+--------+------------------------+---------------+-----------------
450 | Brinna | Barabisch | Female | bbarabischch@apple.com | 1989-10-05 | Afghanistan
656 | Christy | Tew | Female | ctewi7@i2i.jp | 2007-07-18 | Afghanistan
283 | Anallese | Wooles | Female | awooles7u@ebay.co.uk | 2014-02-15 | Afghanistan
709 | Wake | Machans | Male | wmachansjo@nih.gov | 1999-10-04 | Afghanistan
147 | Hercules | Anstie | Male | hanstie42@cdc.gov | 1987-04-18 | Afghanistan
660 | Kissie | Emett | Female | kemettib@trellian.com | 1991-05-23 | Albania
490 | Kaile | Neillans | Female | kneillansdl@t.co | 2002-09-04 | Albania
245 | Lee | Kitter | Female | lkitter6s@youku.com | 1953-12-04 | Albania
361 | Rolf | Capnor | Male | | 1997-01-01 | Albania
590 | Barbaraanne | Easey | Female | beaseygd@sina.com.cn | 2017-02-09 | Albania
(10 rows)
DESC mode
SELECT * FROM person ORDER BY contry_of_birth DESC LIMIT 10;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
-----+------------+-----------+--------+-------------------------+---------------+-----------------
757 | Ansell | Teaz | Male | ateazl0@dmoz.org | 1971-05-02 | Zimbabwe
559 | Bourke | Zamora | Male | bzamorafi@php.net | 1983-07-16 | Zimbabwe
146 | Sharla | Green | Female | sgreen41@live.com | 2020-03-07 | Zambia
860 | Raul | Yeliashev | Male | ryeliashevnv@wiley.com | 2014-02-16 | Zambia
332 | Mack | OLagen | Male | | 1973-04-03 | Yemen
314 | Odessa | Pinnigar | Female | opinnigar8p@4shared.com | 1986-09-05 | Yemen
567 | Avery | Snap | Male | asnapfq@gravatar.com | 1977-02-10 | Yemen
535 | Ruprecht | Lippett | Male | rlippetteu@amazon.co.uk | 1984-03-09 | Yemen
955 | Terrance | Fevier | Male | tfevierqi@myspace.com | 1974-07-11 | Yemen
19 | Emanuel | Bembrigg | Male | | 2016-11-20 | Western Sahara
(10 rows)
Distinct
DISTINCT
removes duplicates from a Query
SELECT DISTINCT $column_name FROM $table_name
SELECT DISTINCT contry_of_birth FROM person ORDER BY contry_of_birth LIMIT 5;
contry_of_birth
---------------------
Afghanistan
Albania
Antigua and Barbuda
Argentina
Armenia
(5 rows)
Where
SELECT $column_A FROM $table WHERE $condtion
SELECT * FROM person WHERE gender = 'Male' LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
----+------------+-----------+--------+-------------------------+---------------+-----------------
1 | Sargent | Matusiak | Male | smatusiak0@irs.gov | 1968-09-08 | China
2 | Tobias | Bulled | Male | | 1961-07-01 | Poland
3 | Wait | Deeves | Male | wdeeves2@lulu.com | 1972-09-24 | Russia
6 | Antonino | Pineaux | Male | | 1971-05-28 | Russia
7 | Quintin | Cromb | Male | qcromb6@theguardian.com | 1979-11-25 | China
(5 rows)
You can also use other comparison operators
SELECT * FROM person WHERE date_of_birth > DATE '1999-03-03' LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
----+------------+-------------+--------+-------------------------------+---------------+-----------------------
5 | Tobye | Blaisdell | Female | tblaisdell4@sciencedirect.com | 2015-10-14 | Netherlands
9 | Barthel | Roughey | Male | broughey8@opensource.org | 2002-04-23 | Philippines
11 | Geralda | Ludgate | Female | | 2005-02-12 | France
12 | Tracey | Connaughton | Female | tconnaughtonb@pcworld.com | 2008-03-05 | Saint Kitts and Nevis
13 | Alica | OMahony | Female | | 2004-02-22 | France
(5 rows)
AND - OR
Conditionals can also be combined using AND & OR.
SELECT ... FROM ... WHERE $condtion_A AND $condtion_B
Chained Conditionals
SELECT * FROM person WHERE date_of_birth > DATE '1999-03-03' AND contry_of_birth = 'Brazil' LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
-----+------------+-----------+--------+--------------------------+---------------+-----------------
30 | Katee | Coopman | Female | kcoopmant@columbia.edu | 2006-10-17 | Brazil
40 | Sula | Hourihane | Female | shourihane13@mozilla.org | 2016-12-06 | Brazil
53 | Camellia | Minico | Female | | 2008-03-26 | Brazil
62 | Leona | Leborgne | Female | lleborgne1p@squidoo.com | 2000-02-29 | Brazil
398 | Davidson | Kynson | Male | dkynsonb1@e-recht24.de | 2002-01-31 | Brazil
(5 rows)
Nested Conditionals
SELECT * FROM person WHERE gender = 'Male' AND (contry_of_birth = 'Brazil' OR contry_of_birth = 'Russia') LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
----+------------+-----------+--------+-----------------------+---------------+-----------------
3 | Wait | Deeves | Male | wdeeves2@lulu.com | 1972-09-24 | Russia
6 | Antonino | Pineaux | Male | | 1971-05-28 | Russia
22 | Berny | Tremayle | Male | | 1989-08-21 | Brazil
37 | Morly | Emanueli | Male | | 1991-06-01 | Russia
51 | Holden | Olliff | Male | holliff1e@cbsnews.com | 1992-12-25 | Russia
(5 rows)
Comparison Operators
Equal | Greater Than | Less Than | Greater Than or Equal To | Less Than or Equal To | Not Equal |
---|---|---|---|---|---|
= | > | < | >= | <= | <> |
Offset
SELECT ... FROM ... OFFSET $n
returns all columns after the first n
columns.
SELECT * FROM person OFFSET 5 LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
----+------------+-----------+--------+--------------------------+---------------+-----------------
6 | Antonino | Pineaux | Male | | 1971-05-28 | Russia
7 | Quintin | Cromb | Male | qcromb6@theguardian.com | 1979-11-25 | China
8 | Rivy | Di Biagio | Female | rdibiagio7@nymag.com | 1987-07-18 | Uruguay
9 | Barthel | Roughey | Male | broughey8@opensource.org | 2002-04-23 | Philippines
10 | Tanhya | Dymock | Female | tdymock9@unesco.org | 1974-04-08 | Ukraine
(5 rows)
Fetch
SQL standard for LIMIT.
LIMIT $n
is equivalent to FETCH FIRST $n ROW ONLY
IN
A shorter way to write OR’d equal to statements.
... WHERE $column_X = $value_1 OR ... OR $column_X = $value_N
Is equivalent to
... WHERE $column_X IN ($value_1, ..., $value_N)
SELECT * FROM person WHERE contry_of_birth IN ('Russia', 'Germany', 'Japan') OFFSET 5 LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
-----+------------+--------------+--------+------------------------------+---------------+-----------------
61 | Aimee | Romei | Female | aromei1o@yahoo.com | 2019-02-17 | Russia
63 | Sybille | Disbrey | Female | sdisbrey1q@addtoany.com | 2004-11-26 | Germany
72 | Jamima | Hardison | Female | jhardison1z@china.com.cn | 1959-04-25 | Russia
90 | Quintin | Raubenheimer | Male | qraubenheimer2h@yolasite.com | 2015-04-22 | Japan
102 | Rand | Oxenden | Male | roxenden2t@123-reg.co.uk | 2003-08-31 | Russia
(5 rows)
Between
Select all rows where the given column is between two values
... WHERE $column BETWEEN $value_1 AND $value_2
SELECT * FROM person WHERE date_of_birth BETWEEN DATE '2000-01-01' AND '2009-12-31' ORDER BY date_of_birth LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
-----+------------+-------------+--------+------------------------------+---------------+-----------------
907 | Marjie | Wrightham | Female | | 2000-02-20 | Portugal
62 | Leona | Leborgne | Female | lleborgne1p@squidoo.com | 2000-02-29 | Brazil
624 | Gennie | De Michetti | Female | gdemichettihb@imdb.com | 2000-03-18 | Sweden
362 | Jedediah | Hincham | Male | jhinchama1@ezinearticles.com | 2000-03-27 | Russia
994 | Gertruda | Tapp | Female | gtapprl@amazon.de | 2000-06-16 | Netherlands
(5 rows)
Like
LIKE
is used for pattern matching strings
... WHERE $column LIKE $pattern
Symbols
%
is the wild card_
Matches a single char
SELECT * FROM person WHERE email LIKE '%@google%' LIMIT 5;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth
-----+------------+-----------+--------+-----------------------+---------------+-----------------
31 | Cherin | Trevett | Female | ctrevettu@google.cn | 2013-03-02 | China
121 | Maggee | Greenley | Female | mgreenley3c@google.pl | 1970-01-23 | China
212 | Jeramie | Margeram | Male | jmargeram5v@google.fr | 1973-03-10 | Indonesia
301 | Alisa | Twells | Female | atwells8c@google.cn | 1987-02-28 | China
305 | Frannie | Marriage | Male | fmarriage8g@google.fr | 2015-10-04 | Russia
(5 rows)
iLike
ILIKE
is a case insensitive version of LIKE
Group By
Groups data by a column
SELECT $column, COUNT(*) FROM $table GROUP BY $column
SELECT contry_of_birth, COUNT(*) from person GROUP BY contry_of_birth ORDER BY COUNT DESC LIMIT 10;
contry_of_birth | count
-----------------+-------
China | 188
Indonesia | 104
Russia | 61
Philippines | 52
Brazil | 46
United States | 37
Portugal | 37
France | 30
Poland | 28
Sweden | 24
(10 rows)
Having
A way to filter GROUP BY
results
... GROUP BY ... HAVING $condtional
SELECT contry_of_birth, COUNT(*) from person GROUP BY contry_of_birth HAVING COUNT(*) > 10 ORDER BY contry_of_birth LIMIT 10;
contry_of_birth | count
-----------------+-------
Argentina | 11
Brazil | 46
China | 188
Colombia | 16
Czech Republic | 11
France | 30
Greece | 14
Indonesia | 104
Japan | 14
Mexico | 13
(10 rows)
Other Aggregate Functions like
COUNT()
can be found here and are discussed in more detail in the next section.
Exporting Query Results
COPY (query) TO 'location/file_name' DELIMITER 'delimiter' FILE_TYPE HEADER;
Example (using the car_person.sql file):
COPY persons TO '\tmp\persons_db.csv' DELIMITER ',' CSV HEADER;
Max, Min, Avg
SELECT MAX(price), MIN(price), AVG(price::numeric)::money FROM car;
max | min | avg
------------+---------+------------
$19,987.90 | $260.86 | $10,039.70
(1 row)
For Avg, the price is cast to a numeric type then back to money because
AVG
does not accept the type money.
Getting the Min, Max and, Avg for each Make & Model
SELECT make, model, MIN(price), MAX(price), AVG(price::numeric)::money FROM car GROUP BY make, model ORDER BY AVG(price::numeric) LIMIT 10;
make | model | min | max | avg
------------+----------------+---------+---------+---------
Volkswagen | GTI | $260.86 | $260.86 | $260.86
Bentley | Continental GT | $277.09 | $277.09 | $277.09
Mitsubishi | Expo | $288.28 | $288.28 | $288.28
Chevrolet | Blazer | $340.02 | $340.02 | $340.02
Mazda | Protege | $387.81 | $387.81 | $387.81
Lincoln | Navigator | $561.70 | $561.70 | $561.70
Mazda | B2600 | $577.52 | $577.52 | $577.52
Dodge | Ram Wagon B150 | $599.64 | $599.64 | $599.64
Porsche | 911 | $660.27 | $660.27 | $660.27
Isuzu | Rodeo | $709.94 | $709.94 | $709.94
(10 rows)
Sum
SELECT make, SUM(price) FROM car GROUP BY make LIMIT 5;
make | sum
----------+-------------
McLaren | $4,512.17
Ford | $799,628.88
Maserati | $67,333.64
Dodge | $582,804.56
Infiniti | $125,195.89
(5 rows)
Operations
SELECT 2+2 AS add,
2-2 AS sub,
2*3 AS mul,
16/2 AS div,
3^3 AS pow,
5! AS fac,
9%3 AS mod;
add | sub | mul | div | pow | fac | mod
-----+-----+-----+-----+-----+-----+-----
4 | 0 | 6 | 8 | 27 | 120 | 0
(1 row)
Operations on Data
Here we are displaying the new price and potential loss a 10% sale plus a $200 coupon would cost a car dealership.
SELECT make, model, year, (price::numeric * .9 - 200)::money AS "New Price", (price::numeric * .1 + 200)::money AS "Lost Profit" FROM car ORDER BY "Lost Profit" LIMIT 10;
make | model | year | New Price | Lost Profit
------------+----------------+------+-----------+-------------
Volkswagen | GTI | 2000 | $34.77 | $226.09
Bentley | Continental GT | 2012 | $49.38 | $227.71
Mitsubishi | Expo | 1992 | $59.45 | $228.83
Ford | Explorer | 2009 | $60.29 | $228.92
Lexus | ES | 2007 | $68.84 | $229.87
Chevrolet | Blazer | 1992 | $106.02 | $234.00
Acura | RSX | 2003 | $123.57 | $235.95
Hyundai | Accent | 1999 | $141.35 | $237.93
McLaren | MP4-12C | 2012 | $148.40 | $238.71
Mazda | Protege | 2001 | $149.03 | $238.78
(10 rows)
Alias
You might have noticed the AS
keyword in the above SQL, its just a way to rename columns in the output.
SELECT $column_name AS $new_column_name FROM ...
Coalesce
COALESCE($val_1, $val_2, ..., $val_n)
will output the first value that is not null
Usage
SELECT COALESCE(null, null, 2)
Can be used to provide a default value.
SELECT email, COALESCE(email, 'Email not Provided') FROM person LIMIT 5;
email | coalesce
-------------------------------+-------------------------------
smatusiak0@irs.gov | smatusiak0@irs.gov
| Email not Provided
wdeeves2@lulu.com | wdeeves2@lulu.com
| Email not Provided
tblaisdell4@sciencedirect.com | tblaisdell4@sciencedirect.com
(5 rows)
NullIf
NULLIF($val_1, $val_2)
If val_1
== val_2
returns Null, otherwise returns val_1
Handling Division by Zero
SELECT COALESCE($val_A / NULLIF(%val_B, 0), -1)
Val_A = 33, Val_B = 11
SELECT COALESCE(33 / NULLIF(11, 0), -1);
coalesce
----------
3
(1 row)
Val_A = 33, Val_B = 0
SELECT COALESCE(33 / NULLIF(0, 0), -1);
coalesce
----------
-1
(1 row)
Now
NOW()
Returns the current Date and Time
Format: yyyy-mm-dd hh:mm:ss.uuuuuu+Shift_From_UTC
SELECT NOW();
now
-------------------------------
2020-04-11 15:58:19.979002-05
(1 row)
Using NOW() to get the date and time
Cast NOW() into DATE and TIME.
SELECT NOW()::DATE AS date, NOW()::TIME AS time;
date | time
------------+-----------------
2020-04-11 | 16:57:12.945428
(1 row)
More Date/Time Info can be found here
Interval
Used to calculate differences between Date/Time objects
SELECT
NOW()::DATE AS now,
(NOW() - INTERVAL '1 YEAR')::DATE AS "1 Year Ago",
(NOW() + INTERVAL '100 DAYS')::DATE as "100 Days From Now";
now | 1 Year Ago | 100 Days From Now
------------+------------+-------------------
2020-04-11 | 2019-04-11 | 2020-07-20
(1 row)
Extract
Used to extract part of a datetime object
SELECT
EXTRACT(CENTURY FROM NOW()) AS Century,
EXTRACT(YEAR FROM NOW()) as Year,
EXTRACT(MONTH FROM NOW()) AS Month,
EXTRACT(DAY FROM NOW()) AS Day,
EXTRACT(DOW FROM NOW()) AS "Day Of The Week";
century | year | month | day | Day Of The Week
---------+------+-------+-----+-----------------
21 | 2020 | 4 | 11 | 6
(1 row)
Where the Day of the Week uses the following table:
Sunday | Monday | ... | Friday | Saturday |
---|---|---|---|---|
0 | 1 | ... | 5 | 6 |
Age
AGE
Calculates the difference between two timestamps
SELECT first_name, last_name, AGE(NOW(), date_of_birth) AS Age FROM person ORDER BY date_of_birth LIMIT 5;
first_name | last_name | age
------------+-----------+-----------------------------------------
Isabelle | Etheredge | 70 years 2 mons 25 days 17:24:41.850913
Holly-anne | Folker | 70 years 2 mons 22 days 17:24:41.850913
Edeline | Pietz | 70 years 1 mon 7 days 17:24:41.850913
Neall | Danbrook | 70 years 21 days 17:24:41.850913
Leesa | Gerault | 69 years 9 mons 25 days 17:24:41.850913
(5 rows)
Prefix
\d $table
will show the columns and indexes for a table. Use this to see the constraints and primary keys.
Primary Key
Adding a Primary Key
On Table Creation
create table person (
id BIGSERIAL NOT NULL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
gender VARCHAR(7) NOT NULL,
email VARCHAR(100),
date_of_birth DATE NOT NULL,
contry_of_birth VARCHAR(50) NOT NULL
);
One of the value constraints is PRIMARY KEY
After Table Creation
ALTER TABLE $table_name ADD PRIMARY KEY ($pk_column);
Notes:
- All values in the
pk_column
must be unique - You can use multiple columns instead of one to make a pkey
Removing a Primary Key
ALTER TABLE $table DROP CONSTRAINT $table_pkey;
UUIDs as Primary Keys
Generating UUIDs
Postgres automatically comes with an extension you can enable to use UUIDs. Simply run
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
SELECT uuid_generate_v4();
uuid_generate_v4
--------------------------------------
0a59ea41-ad36-4a23-85d0-eea04c72a519
(1 row)
Inserting Rows
In the CREATE TABLE
:
CREATE TABLE $table_name (
id UUID PRIMARY KEY,
$column_1,
...,
$column_N
);
When Inserting:
INSERT INTO $table (id, $column_1, ..., $column_N) VALUES (uuid_generate_v4(), $value_1, ..., $value_N);
Constrains
Unique
Different from primary keys, but have the same add/drop syntax.
Add
For a custom constraint name use:
ALTER TABLE $table ADD CONSTRAINT $constraint_name UNIQUE ($column);
To let PostgreSQL decide the constraint name use:
ALTER TABLE $table ADD UNIQUE ($column);
Remove
ALTER TABLE $table DROP CONSTRAINT $constraint_name;
Check
A Constraint based on a condition.
ALTER TABLE $table ADD CONSTRAINT $constraint_name CHECK ($conditional);
An example using the person
table.
ALTER TABLE person ADD CONSTRAINT gender_constraint CHECK (gender IN ('Female', 'Male'));
\d person
Table "public.person"
Column | Type | Collation | Nullable | Default
-----------------+------------------------+-----------+----------+------------------------------------
id | bigint | | not null | nextval('person_id_seq'::regclass)
first_name | character varying(50) | | not null |
last_name | character varying(50) | | not null |
gender | character varying(7) | | not null |
email | character varying(100) | | |
date_of_birth | date | | not null |
contry_of_birth | character varying(50) | | not null |
Indexes:
"person_pkey" PRIMARY KEY, btree (id)
"unique_email" UNIQUE CONSTRAINT, btree (email)
Check constraints:
"gender_constraint" CHECK (gender::text = ANY (ARRAY['Female'::character varying, 'Male'::character varying]::text[]))
On Conflict
Used to manage update/insert errors where there is a unique constraint on a column.
In the SQL below if the primary key is a duplicate, it is not inserted into the table, instead, nothing happens.
INSERT INTO $table ($pk_column, $column_1, $column_2, ..., $column_N)
VALUES ($pk_value, $value_1, $value_2, ..., $value_N)
ON CONFLICT ($pk_column) DO NOTHING;
To manage more than one unique column use:
ON CONFLICT ($unique_column_1, $unique_column_2) DO NOTHING;
Delete
DELETE FROM $table WHERE ...
Deleting when a Foreign Key is present
Two Options:
- Update record dependent on foreign key.
- Delete record dependent on foreign key.
- Use Cascade - This is a bad practice.
Then delete the record belonging to the foreign key.
Update
UPDATE $table SET $column = $value WHERE ...
For more than one column:
UPDATE $table SET $column_1 = $value_1, $column_2 = $value_2 WHERE ...
Upsert
Used when a row may or may not be present in the database.
For example if a user updates their email shortly after creating their account, and the order that the database receives these requests is not known. Only the most up-to-date info will be kept. Regardless of which gets there first.
INSERT INTO $table ($pk, $column_1, ... $column_N) VALUES ($pk, $value_1, ... $value_2)
ON CONFLICT ($pk) DO UPDATE SET $column_to_update = EXCLUDED.$column_to_update;
Foreign Key
A Foreign Key is a column in a table that links to the primary key of a row in another column.
Foreign Key row example:
$forign_key $TYPE REFERANCES $table($pk) UNIQUE $forgin_key
In an INSERT statement:
create table person (
id BIGSERIAL NOT NULL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
gender VARCHAR(7) NOT NULL,
email VARCHAR(100),
date_of_birth DATE NOT NULL,
contry_of_birth VARCHAR(50) NOT NULL,
car_id BIGINT REFERENCES car(id) UNIQUE (car_id)
);
Note - The Forging Key must be the same type as the pk it references.
Updating
UPDATE person SET $fk = $val WHERE ...;
Joins
Notes:
- The
ON $table_1.column = $table_2.column
syntax is the same asUSING $column
if and only if the columns have the same names in both tables.
Inner Joins
Combines elements present in both tables into a new table.
SELECT * FROM $table_1 JOIN $table_2 ON $table_1.column = $table_2.column;
An example using our person
and car
tables.
SELECT * FROM person JOIN car ON person.car_id = car.id;
-[ RECORD 1 ]---+-------------------
id | 3
first_name | Wait
last_name | Deeves
gender | Male
email | wdeeves2@lulu.com
date_of_birth | 1972-09-24
contry_of_birth | Russia
car_id | 1
id | 1
make | Acura
model | Integra
year | 1995
vin | JHMZE2H59DS943694
price | $3,441.88
-[ RECORD 2 ]---+-------------------
id | 1
first_name | Sargent
last_name | Matusiak
gender | Male
email | smatusiak0@irs.gov
date_of_birth | 1968-09-08
contry_of_birth | China
car_id | 3
id | 3
make | BMW
model | 7 Series
year | 2001
vin | 5J8TB3H33FL023983
price | $19,746.03
Selecting specific columns from both tables:
SELECT person.first_name, person.last_name, car.make, car.model FROM person JOIN car ON person.id = car.id;
first_name | last_name | make | model
------------+-----------+-------+----------
Sargent | Matusiak | Acura | Integra
Tobias | Bulled | Acura | Integra
Wait | Deeves | BMW | 7 Series
(3 rows)
Left Joins
Combines two tables where the result has everything from the Left table and the shared values from the right table.
In the Car example the result would have all the people and the people who have cars would also have the car info.
The people without a car_id
will have null
for all of the car values.
SELECT * FROM person LEFT JOIN car ON car.id = person.car_id;
id | first_name | last_name | gender | email | date_of_birth | contry_of_birth | car_id | id | make | model | year | vin | price
----+------------+-----------+--------+-------------------------------+---------------+-----------------+--------+----+-------+----------+------+-------------------+------------
3 | Wait | Deeves | Male | wdeeves2@lulu.com | 1972-09-24 | Russia | 1 | 1 | Acura | Integra | 1995 | JHMZE2H59DS943694 | $3,441.88
1 | Sargent | Matusiak | Male | smatusiak0@irs.gov | 1968-09-08 | China | 3 | 3 | BMW | 7 Series | 2001 | 5J8TB3H33FL023983 | $19,746.03
5 | Tobye | Blaisdell | Female | tblaisdell4@sciencedirect.com | 2015-10-14 | Netherlands | | | | | | |
4 | Lulita | Olivo | Female | | 1953-09-03 | China | | | | | | |
2 | Tobias | Bulled | Male | | 1961-07-01 | Poland | | | | | | |
(5 rows)