What is Relational Database Design?
Stages of Relational Database Design
Define the purpose & entitites of the Relational DB.
Identify the Primary keys.
Establish Table Relationships.
join table
Apply Normalization Rules.
Normalization
: Process of optimizing the database structure so that redundancy and confusion are eliminated.Using Transactions allow us to make changes to a SQL database in a consistent and durable way, and it is considered best practice to use them regularly.
Transaction
: Single unit of work, which can contain multiple operations, performed on a database.
Implicit vs. explicit transactions
implicit
transaction.explicit
transactions will allow us to create save points and roll back to whatever point in time we choose.PostgreSQL transactional commands
BEGIN
: Initiates a transaction block. All Statements after a BEGIN command will be executed in a single transaction until COMMIT or ROLLBACK is called.COMMIT
: Commits a current transaction, all changes made by this transaction become visible to others and are guaranteed to be durable if a crash occurs.ROLLBACK
: Rolls back current transaction and removes all updates made by the transaction.BEGIN;
INSERT INTO hobbits(name,purpose)
VALUES('Frodo','Destroy the One Ring of power.');
ROLLBACK;
SAVEPOINT
: Establishes a new save point within the current transaction.BEGIN;
DELETE FROM fellowship
WHERE age > 100;
SAVEPOINT first_savepoint;
DELETE FROM fellowship
WHERE age > 80;
DELETE FROM fellowship
WHERE age >= 40;
ROLLBACK TO first_savepoint;
COMMIT;
SET TRANSACTION
: Sets the characteristics of the current transaction.When to use transactions and why
Good to use when making any updates, insertions, or deletions to a database.
Help us deal with crashes, failures, data consistency, and error handling.
Atomicity
is another feature that is a benefit of transactions.
Transaction properties: ACID
ACID
(Atomic, Consistent, Isolated, and Durable)Atomicity
: All changes to data are performed as if they are a single operation.
Consistency
: Data is in a consistent start when a transaction starts and ends.
Isolation
: Intermediate state of a transaction is invisible to othe rtransactioned, they appear to be serialized.
Durable
: After a transaction successfully completes, changes to data persists and are not undone even in system failure.
Banking transaction example
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');
COMMIT;
Joins vs. Subqueries What is a JOIN?
Inner Join
: Returns results containing rows in the left table that match rows in the right table.Left Join
: Returns a set of results containing all rows from the left table with the matching rows from the right table. If there is no match, the right side will have null values.Right Join
: Returns a set of results containing all rows from the right table with the matching rows from the left table. If there is no match, the left side will have null values.Full Outer Join
: Returns a set of results containing all rows from both the left and right tables, with matching rows from both sides where avail. If there is no match the missing side contains null values.Self-Join
: Query in which a table is joined to itslef, useful for comparing values in a column of rows within the same table.What is a subquery?
postgres=# SELECT * FROM puppies;
id | name | age_yrs | breed_id | weight_lbs | microchipped
----+----------+---------+----------+------------+--------------
1 | Cooper | 1.0 | 8 | 18 | t
2 | Indie | 0.5 | 9 | 13 | t
3 | Kota | 0.7 | 1 | 26 | f
4 | Zoe | 0.8 | 6 | 32 | t
5 | Charley | 1.5 | 2 | 25 | f
6 | Ladybird | 0.6 | 7 | 20 | t
7 | Callie | 0.9 | 4 | 16 | f
8 | Jaxson | 0.4 | 3 | 19 | t
9 | Leinni | 1.0 | 8 | 25 | t
10 | Max | 1.6 | 5 | 65 | f
(10 rows)
SELECT
puppies.name,
age_yrs,
breeds.name
FROM
puppies
INNER JOIN
breeds ON (breeds.id = puppies.breed_id)
WHERE
age_yrs > (
SELECT
AVG (age_yrs)
FROM
puppies
);
Multiple-row subquery
SELECT *
FROM friends
INNER JOIN puppies ON (puppies.puppy_id = friends.puppy_id)
WHERE
puppies.age_yrs < 0.6;
Should I use a JOIN or a subquery?
Joins are better when you want to combine rows from one or more tables based on a match condition.
Subqueries work great when you’re only returning a single value.
When returning multiple rows, you could go with either subQ’s or joins.
PostgreSQL Index
: Works similarly like an index in the back of a book, they provide special lookup tables to let us make faster database queries.
The Syntax for creating index is as follows:
CREATE INDEX index_name ON table_name (column_name);
CREATE INDEX addresses_phone_index ON addresses (phone_number);
DROP INDEX addresses_phone_index;
Types of indexes
There are several types of indexes use in Postgres: B-tree, Hash, GiST, and GIN.
Single-Column Indexes
Uses only one table column.
CREATE INDEX addresses_phone_index ON addresses (phone_number);
Multiple-Column Indexes
Uses more than one table column.
CREATE INDEX idx_addresses_city_post_code ON table_name (city_id, postal_code);
Partial Indexes
Uses subset of a table defined by a conditional expression.
CREATE INDEX addresses_phone_index ON addresses (phone_number) WHERE (city_id = 2);
When to use an index
Overall DB indexes are known to enhance performance when performing queries, however there are certain situations where it is not recommended:
Using EXPLAIN and ANALYZE keywords can give us performance data of our queries.
EXPLAIN ANALYZE SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=2.37..553.11 rows=106 width=488) (actual time=1.392..12.700 rows=100 loops=1)
-> Bitmap Heap Scan on tenk1 t1 (cost=2.37..232.35 rows=106 width=244) (actual time=0.878..2.367 rows=100 loops=1)
Recheck Cond: (unique1 < 100)
-> Bitmap Index Scan on tenk1_unique1 (cost=0.00..2.37 rows=106 width=0) (actual time=0.546..0.546 rows=100 loops=1)
Index Cond: (unique1 < 100)
-> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.00..3.01 rows=1 width=244) (actual time=0.067..0.078 rows=1 loops=100)
Index Cond: (unique2 = t1.unique2)
Total runtime: 14.452 ms
Node-Postgres And Prepared Statements
Connecting
const { Pool } = require("pg");
const pool = new Pool({
user: "application_user",
password: "iy7qTEcZ",
});
const results = await pool.query(`
SELECT id, name, age_yrs
FROM puppies;
`);
console.log(results);
Prepared Statement
in inside an array.
await pool.query(`
INSERT INTO puppies (name, age_yrs, breed, weight_lbs, microchipped)
VALUES ($1, $2, $3, $4, $5);
`, [name, age, breedName, weight, isMicrochipped]);
const { Pool } = require("pg");
const allPuppiesSql = `
SELECT id, name, age_yrs, breed, weight_lbs, microchipped
FROM puppies;
`;
const pool = new Pool({
database: "«database name»",
});
async function selectAllPuppies() {
const results = await pool.query(allPuppiesSql);
console.log(results.rows);
pool.end();
}
const id = Number.parseInt(process.argv[2]);
selectOnePuppy(id);