6.3 KiB
PostGres - Advanced
Lesson Objectives - important
- Linking Tables
- Alias
- Indexes
- Default Values
- Constraints
- Distinct
Lesson Objectives - good to know about
- EER Diagrams
- Unions
- Truncate
- Views
- Functions
- Stored Procedures
- Triggers
- Transactions
- Locks
- Privileges
- Denormalization
- Excel -> CSV -> SQL
Important
Linking Tables
You can have lots of different kinds of relationships between your data. Here are the most common. This page has some nice ways to visualize the data
- One to Many/Many to One Relationships
- This is the most common
- One
customercan have manyorders - Many
orderscan belong to onecustomer - Each
orderrow contains acustomer_idcolumn, which denotes whichcustomerthey belong to
- Many to Many Relationships
- Not as common, but still prevalent
- Each
actoris in manymovies - Each
moviehas manyactorsin it - A "linking table" exists which maps actors to movies
- It contains only an
actor_idand amovie_idcolumn - You'll need to join
actorstoactors_movies_linkerand then join that again tomovies(yes, you can have multiple joins in one query)
- It contains only an
- Self Referncing Relationships
- Customer referral system
- Each
customerhas areferal_idwhich points back to thecustomertable - This is just like the One to Many relationship, but both tables are the same
- One to One Relationships
- Seats at a theater
- Each
personhas only oneseat - Each
seathas only oneperson - This is just like a One to Many relationship, but there are no duplicate values in the foreign key column
Alias
Sometimes table/column names can become difficult to read. When this happens, sometimes it is easier to temporarily rename them within the context of the query. Note this does not change anything in memory.
SELECT t1.column1 as col1, t2.column2 as col2
FROM table1 as t1
INNER JOIN table2 as t2
ON t1.common_filed = t2.common_field;
This can also help when you use the same table twice in one query. Say you had a message tracker database. This would be a Many to Many relationship, where the messages would act as the linking table. It would contain Foreign Key references for both sender_id and receiver_id, but each column would point to the same people table. In order to reference columns correctly, you would need to rename the people table at least once
SELECT *
FROM people AS senders
JOIN messages
ON messages.sender_id = senders.id
JOIN people AS receivers
ON messages.receiver_id = receivers.id
Indexes
CREATE INDEX index_name ON table_name (column_name);CREATE INDEX index_name ON table_name (column1_name, column2_name);- Primary Key
- use
\d table_nameto view indexes
Default Values
CREATE TABLE people (id SERIAL, name VARCHAR(16), age INT DEAFULT 0);
\d people
INSERT INTO people (name) VALUES ('matt');
SELECT * FROM people;
Constraints
- NOT NULL
- Unique
- Foreign Keys
CREATE TABLE companies(
id SERIAL,
name VARCHAR(16) NOT NULL,
city VARCHAR(16)
);
INSERT INTO companies ( city ) VALUES ('Palo Alto');
CREATE TABLE people(
id INT,
name VARCHAR(16),
email VARCHAR(32) UNIQUE,
company_id INT REFERENCES companies(id)
);
\d people
INSERT INTO people (name, email, company_id) VALUES ('bob', 'bob@bob.com', 999) -- bad company_id
INSERT INTO people (name, email, company_id) VALUES ('bob', 'bob@bob.com', 1) -- not unique email
Distinct
SELECT DISTINCT city FROM people;
Good to Know About
EER Diagrams
Unions
SELECT name FROM people UNION SELECT name FROM companies; -- show distinct values
SELECT name FROM people UNION ALL SELECT name FROM companies; -- show duplicates
Truncate
TRUNCATE TABLE people; -- delete all data, but don't delete table itself
Views
CREATE VIEW new_yorkers AS SELECT * FROM people WHERE city = 'NYC';
\dv
SELECT * FROM new_yorkers
Functions
CREATE FUNCTION add_numbers(a integer, b integer) RETURNS integer AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
\df
SELECT add_numbers(2,4);
Stored Procedures
CREATE PROCEDURE add_person(new_name VARCHAR(16))
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO people (name) VALUES (new_name);
END
$$;
\df
call add_person('matt');
Triggers
CREATE TABLE backup_people (id INT, name VARCHAR(16), age INT);
CREATE FUNCTION moveDeleted() RETURNS trigger AS $$
BEGIN
INSERT INTO backup_people VALUES (OLD.id, OLD.name, OLD.age);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER archive_person
BEFORE DELETE on people
FOR EACH ROW
EXECUTE PROCEDURE moveDeleted();
\df
DELETE FROM people WHERE id = 1;
Transactions
BEGIN;
INSERT INTO people (name) VALUES ('matt');
SELECT * FROM people;
-- start a different session and run SELECT * FROM people;
-- Switch back to original session
COMMIT;
-- in other session run SELECT * FROM people;
OR
BEGIN;
INSERT INTO people (name) VALUES ('matt');
SELECT * FROM people;
asdfasdfasdfasdfasdfasdf;
ROLLBACK;
SELECT * FROM people;
Locks
BEGIN;
LOCK TABLE people IN ROW EXCLUSIVE MODE;
SELECT * FROM people WHERE id = 12 FOR UPDATE;
-- start a new session and run UPDATE people SET name = 'Matt' WHERE id = 12;
-- switch back to original session
UPDATE people SET age = 43 WHERE id = 12;
SELECT * FROM people;
END;
Privileges
CREATE USER youruser;
\du
-- new session
psql -U youruser -d supertest_lab
SELECT * FROM people;
-- original session
GRANT ALL ON people TO youruser;
-- switch sessions again
SELECT * FROM people;
Denormalization
| id | name | age | company_id | company_name | company_address |
|---|---|---|---|---|---|
| 1 | matt | 43 | 1 | SF |
Excel -> CSV -> SQL
- Create sheet
- File -> Download -> .csv
COPY people (name, age, ancestry, city) FROM '/Users/matthuntington/Downloads/people.csv' DELIMITER ',' CSV;
SQL Injection
- input with value
Huntington'; DROP TABLE people;
