Skip to content

Instantly share code, notes, and snippets.

@neall
Created February 22, 2019 20:35
Show Gist options
  • Save neall/bcefa3e6484f5cbca1d56dee66c3643f to your computer and use it in GitHub Desktop.
Save neall/bcefa3e6484f5cbca1d56dee66c3643f to your computer and use it in GitHub Desktop.
-- <- double dash is the SQL way to say "the rest of this line is a comment"
-- Join tables are how you do many-to-many relationships
-- in relational databases, and they're just complicated
-- enough that it puts some people off. But once you get
-- used to them, I don't think they're too bad.
-- ** Creating Our Tables **
CREATE TABLE articles (
id BIGSERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
body VARCHAR NOT NULL
);
-- Defining a field as bigserial is shorthand for making it
-- use bigint and creating an auto-incrementing sequence it
-- will default to.
-- Also when you declare a field as a primary key, that
-- automatically makes it both UNIQUE and NOT NULL.
--
-- You can limit your varchar fields by saying something like
-- varchar(32) if you're *sure* you'll never need more than 32
-- characters, but Postgres handles unconstrained varchar
-- columns pretty efficiently. And there are other string
-- types, but you mostly just want to use varchar nowadays.
-- Also, NOT NULL fields can still be empty string, so watch
-- out for that.
CREATE TABLE tags (
id BIGSERIAL PRIMARY KEY,
tagname VARCHAR UNIQUE NOT NULL
);
CREATE TABLE article_tags (
article_id BIGINT NOT NULL REFERENCES articles(id),
tag_id BIGINT NOT NULL REFERENCES tags(id),
PRIMARY KEY (article_id, tag_id)
);
-- REFERENCES creates a foreign key relationship. This prevents
-- you from inserting `article_id`s that don't exist in the `id`
-- field of `articles` or `tag_id`s that aren't in the `tags(id)`
-- column.
--
-- In the first two tables, we specified our single-column
-- primary key in-line, but if you want a composite primary
-- key, you specify it like this.
-- ** Inserting Our Data **
INSERT INTO articles (title, body)
VALUES (
"Ten Amazing Query Facts You Won't Believe!",
"1. Did you know that SQL..."
)
RETURNING id;
-- The `RETURNING` clause on `INSERT` doesn't exist in every
-- db engine - some databases make you make a second query to
-- figure out what your auto-generated `id` was.
--
-- We'll say that this query returns [{id: 1}]
INSERT INTO tags (tagname)
VALUES
("clickbait"),
("database")
RETURING id;
-- Since we inserted two rows here, this query will
-- return [{id: 1}, {id: 2}]
INSERT INTO article_tags (article_id, tag_id)
VALUES
(1, 1),
(1, 2)
-- associate both our tags with our article
-- ** querying our data **
SELECT
articles.id,
title,
body
FROM
articles
INNER JOIN article_tags ON articles.id = article_id
INNER JOIN tags ON tags.id = tag_id
WHERE tagname = "database";
-- when you join tables, you can filter on and/or select
-- fields from each table. If the column name would be
-- ambiguous (articles and tags both have id columns) you
-- have to specify in the full "tablename.columnname" format.
--
-- This will return [{id: 1, title: "Ten amaz...", body: "1. Did you..."}]
-- (ignore that I didn't want to write out the whole strings again).
-- Important thing about inner joins: they give you back all the
-- matches on each side. Above we filtered back down to just one result
-- with our `WHERE` clause. But compare to this example with no `WHERE` clause:
SELECT
articles.id AS aid,
title,
body,
tags.id AS tid,
tagname
FROM
articles
INNER JOIN article_tags ON aid = article_id
INNER JOIN tags ON tid = tag_id
-- This will return
-- [
-- {aid: 1, title: "Ten amaz...", body: "1. Did you...", tid: 1, tagname: "clickbait"},
-- {aid: 1, title: "Ten amaz...", body: "1. Did you...", tid: 2, tagname: "database"}
-- ]
-- Also note that I gave `articles.id` and `tags.id` aliases using
-- the `AS` keyword. This is useful in my result and also so I
-- don't have to keep writing the long form later in the query.
-- You can use `AS` to make aliases for table names as well, which
-- can save you some typing but is also practically required if
-- you need to join against the same table more than once.
-- There are a *lot* more things you can do with `SELECT`s - I've
-- basically just shown a minimal many-to-many relationship here.
-- Postgres has excellent docs. If you're looking for full
-- references for specific commands, look at:
-- https://www.postgresql.org/docs/current/sql-commands.html
-- but for a more topic-relevant browsable docs look at:
-- https://www.postgresql.org/docs/current/sql.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment