-
DBMS : database management system
- Relational | RDBMS : store in table and link to each other
- MySQL | SQL Server | Oracle ...
- No Relational :
- MongoDB
- Relational | RDBMS : store in table and link to each other
-
Install MySQL: mysql mysql workbench
-
select statement
- the order of the statement matters
- line has to be eneded with ;
- keywords: USE SELECT FROM WHERE ORDER BY
use sql_store; select * from customers -- where customer_id = 1 order by first_name
-
SELECT clause
- apply changes to table
- select distinct vlaue from a column
- create a new column based on another column
- SQL keywords are CAP
SELECT last_name, first_name, points, (poinits +10) * 100 AS "discount factor" FROM customers SELECT DISTINCT state FROM customers
-
WHERE clause
- it is used to compare data comparison operators: >= <= = ( != <>)
SELECT * FROM orders WHERE points > 3000 -- WHERE points > 3000 -- WHERE state = "VA" -- WHERE state = "va" -- WHERE order_date >= "2019-01-01"
-
OR, AND, NOT operator AND has high precedence than OR
SELECT * FROM orders WHERE points > 3000 AND birth_date >= "2019-01-01" -- WHERE points > 3000 OR birth_date >= "2019-01-01" -- WHERE points > 3000 OR birth_date >= "2019-01-01" AND state = "VA" -- WHERE points > 3000 OR (birth_date >= "2019-01-01" AND state = "VA") -- WHERE NOT (points > 3000 OR birth_date > "2019-01-01") -- WHERE points <= 3000 OR birth_date <= "2019-01-01"
-
IN operator / NOT IN operator
SELECT * FROM orders -- WHERE prov = "VA" OR prov = "GA" OR prov = "FL" WHERE prov IN ("VA", "GA", "FL") WHERE prov NOT IN ("VA", "GA", "FL")
-
BETWEEN operator
SELECT * FROM customers -- WHERE points >= 1000 AND points <= 3000 WHERE points BETWEEN 1000 AND 3000 WHERE birth_date BETWEEN "1990-01-01" AND "2000-01-01"
-
LIKE operator match string pattern % : multi characters _ : single character
SELECT * FROM customers WHERE last_name LIKE "b%" -- starts with b or B WHERE last_name LIKE "brush%" -- starts with brush WHERE last_name LIKE "%b%" -- contains b WHERE last_name LIKE "%y" -- ends with y WHERE last_name LIKE "_y" -- 2 letters, ends with y WHERE last_name LIKE "__y" -- 3 letters, ends with y WHERE last_name LIKE "b__y" -- 4 letters, starts with b ends with y
-
REGEXP operator
SELECT * FROM customers WHERE first_name REGEXP "elka| ambur" -- contians elka or ambur WHERE last_name REGEXP "ey$|on$" -- ends with ey or on WHERE last_name REGEXP "^my|se" -- starts with my or contains se WHERE last_name REGEXP "b[ru]" -- container b that follows by r or u WHERE last_name REGEXP "br|bu" -- container b that follows by r or u
-
IS NULL operator
SELECT * FROM customers WHERE phone IS NULL WHERE phone IS NOT NULL
-
ORDER BY clause default: sorted by primary key | yellow key
SELECT * quantity * unit_price AS total_price FROM customers ORDER BY first_name ORDER BY first_name DESC ORDER BY lastname, first_name ORDER BY lastname DESC, first_name DESC ORDER BY quantity * unit_price DESC ORDER BY total_price DESC
-
LIMIT clause LIMIT comes as last
SELECT FROM customers LIMIT 3 LIMIT 6, 3
-
inner join
- keywords: inner | alias | ON | prefix with table name
- prefix with table name, if both table has same column
- INNER keyword is optional
- o & c are alias
- if table alias are set, then you can only use alias
SELECT order_id, o.customer_id, firstname, lastname FROM orders o INNER JOIN customers c ON o.customer_id = c.customer_id
-
join across db
- prefix database name that are not the current database
SELECT * FROM orders_items oi JOIN sql_inventory.products p -- the other db's table ON oi.product_id = p.product_id
-
self joins
USE sql_hr; SELECT e.employee_id, e.firstName, m.firstName AS manager FROM employees e JOIN employees m ON e.reports_to =m.employee_id
-
join multiple tables
use sql_store; -- database SELECT o.order_id, o.order_date, c.last_name, os.name AS order_status FROM orders o -- table base JOIN customers c -- table a ON o.customer_id = c.customer_id JOIN order_status os -- table b ON o.status = os.order_status
-
compound join conditions use two column value to identify a row. | when there are multiple primary keys
SELECT * FROM order_items oi JOIN oreder_item_notes oin ON oi.order_id = oin.order_id AND oi.product_id = oin.product_id
-
implicit join syntax
SELECT order_id, o.customer_id, firstname, lastname FROM orders o INNER JOIN customers c ON o.customer_id = c.customer_id
SELECT * FROM orders o, customers c WHERE o.customer_id = c.customer_id -- IF WHERE IS MISSING : 10*10
-
outer joins
- INNER JOIN : only rows that match ON are listed
- OUTER JOIN
- LEFT OUTER JOIN : FROM SHWN ALL
- RIGHT OUTER JOIN : JOIN SHOWN ALL AVOID RIGHT JOIN
- INNER AND OUTER KEYWORDS CAN BE IGNORED
SELECT order_id, o.customer_id, firstname, lastname FROM orders o LEFT OUTER JOIN customers c ON o.customer_id = c.customer_id
-
outer join between multiple tables
SELECT order_id, o.customer_id, firstname, lastname FROM orders o LEFT OUTER JOIN customers c ON o.customer_id = c.customer_id LEFT OUTER JOIN shipper sh ON c.shipper_id = sh.shipper_id
-
slef outer joins
USE sql_hr; SELECT e.employee_id, e.firstName, m.firstName AS manager FROM employees e LEFT JOIN employees m ON e.reports_to =m.employee_id
-
USING clasue
- when the two tables's column name are the same
SELECT o.order_id, c.first_name, sh.name AS shipper FROM orders o JOIN customer c USING (customer_id) LEFT JOIN shippers sh USING (shipper_id)
-
natural joins
SELECT o.order_id, c.first_name FROM orders o NATURAL JOIN customers c
-
cross joins
SELECT c.first_name AS customer, p.name AS product FROM customers c CROSS JOIN products p
SELECT c.first_name AS customer, p.name AS product FROM customers c, products p
-
unions
- combine data from multiple queries
SELECT order_id, order_date, "Active" AS status FROM orders WHERE order_date >="2010-01-01" UNION SELECT order_id, order_date, "Archived" AS status FROM orders WHERE order_date < "2010-01-01"
-
column attributes
- Column Name
- Datatype
- PK : primary key
- NN: not null
- AI : auto increment
- Default
-
insert a row
- match all colummns
INSERT INTO customers VALUE (DEFAULT, 'john', 'smith', NULL)
- match provided columns
INSERT INTO customers (first_name, last_name, city) VALUE ( 'john', 'smith', 'antwerp')
-
insert multiple rows
INSERT INTO customers (name) VALUES ('john'), ('mark'), ('maxi')
-
insert data into multiple table
INSERT INTO orders (customer_id, order_date) -- PARENT TABLE VALUES (1, '2023-04-03'); INSERT INTO order_items -- CHILD TABLE VALUES (LAST_INSERT_ID(), 1, 1, 2,95) (LAST_INSERT_ID(), 1, 1, 2,95)
-
create a copy of a Table
CREATE TABLE invoice_archived AS SELECT * FROM invoice -- Truncate table if need it empty INSERT INTO invoice_archived -- Query SELECT * -- SUB Query FROM invoices WHERE order_date < '2023-04-03' -- OR: CREATE TABLE invoice_archived AS SELECT * FROM invoices WHERE order_date < '2023-04-03'
-
update a single row / multiple rows
UPDATE invoices SET payment_total = 200 payment_date = due_date WHERE invoice_id = 3 --single row WHERE client_id = 3 -- multiple row WHERE client_id IN (3,4) -- multiple row
-
use sub query in update
UPDATE orders SET comments = 'gold customer' WHERE customer_id IN (SELECT customer_id --sub query FROM customers WHERE points > 3000) WHERE customer_id IN (SELECT customer_id --sub query FROM customers WHERE prov IN ('CA', 'NY')) WHERE payment_date is NULL -- is NULL is sub query
-
delete
DELETE FROM invoices WHERE client_id = ( SELECT * FROM clients WHERE client_name = 'Myworks' )
-
Aggregate Functions
SELECT MAX(invoice_total) AS highest, MIN(invoice_total) AS lowest, AVG(invoice_total) AS average, SUM(invoice_total * 1.1 ) AS total, COUNT(invoice_total) AS number_of_invoices -- only non-null rows COUNT(*) AS total_records -- all rows COUNT(DISTINCT client_id) AS total_records_user FROM invoices WHERE invoice_data > '2019-01-01'
-
GROUP BY Clause
- the order of the clause matters
- group by can not use alias name
SELECT client_id, SUM(invoice_total) AS total_sales FROM invoices WHERE invoice_date >= '2019-07-01' GROUP BY client_id --grouop total sales by client_id ORDER BY total_sales DESC
-
HAVINIG Clause
- use WHERE before group by, use HAVING after GROUP BY
SELECT client_id, SUM(invoice_total) AS total_sales FROM invoices WHERE invoice_date >= '2019-07-01' GROUP BY client_id HAVING invoice_total > 500 -- single HAVING invoice_total > 500 AND invoice_date > '2020-07-01' -- compound -- HAVING Clause can only use VARIABLE indicated in SELECT Clause
-
ROLLUP Operator
- only available in MySQL
- get sum from each group
SELECT prov, city, SUM(invoice_total) AS total_sales FROM invoices i JOIN client c USING (client_id) GROUP BY prov, city WITH ROLLUP
-
Subqueries
- evaluate sub query and pass it to parent
- sub query can be written in SELECT , FROM, WHERE
SELECT * FROM products WHERE unit_price > ( SELECT unit_price FROM products WHERE prodcut_id = 3 )
-
The IN Operator
SELECT * FROM products WHERE product_id NOT IN ( SELECT DISTINCT product_id FROM order_items )
-
Subqueries vs Joins
- Subqueries & Joins can achieve the same thing
- question: Find customers that have ordered lettuce (id=3), select customer_id, first_name, last_name
-- sub query SELECT * FROM customers WHERE customer_id IN ( SELECT o.customer_id FROM order_items oi JOIN orders o USING (order_id) WHERE product_id = 3 )
-- join SELECT DISTINCT customer_id, first_name, last_name FROM customer c JOIN orders o using (customer_id) JOIN order_items oi USING (order_id) WHERE oi.prodcut_id =3
-
The ALL Keyword
SELECT * FROM invoices WHERE invoice_total > ALL( -- ALL compares all items of returned list ALL(100, 200, 3) SELECT invoice_total -- sub query returns a list of item FROM invoices WHERE client_id =3 )
-
The ANY Keyword
SELECT * FROM clients WHERE client_id = ANY( -- EQUAL TO: WHERE client_id IN ( SELECT client_id FROM invoices GROUP BY client_id HAVING COUNT(*) >= 2 )
-
Correlated Subqueries
- compare with self in sub query
SELECT * FROM employees e WHERE salary > ( SELECT AVG(salary) FROM employees WHERE office_id = e.office_id )
-
The EXISTS Operator
-- IN SELECT * FROM clients WHERE client_id IN ( -- EQUALS TO, WHERE client_id IN (1,2,3, SELECT DISTINCT client_id FROM invoices )
-- EXISTS // performance better SELECT * FROM clients c WHERE client_id EXISTS ( -- SUB QUERY DOES NOT RETURN VALUE SELECT DISTINCT client_id FROM invoices WHERE client_id = c.client_id )
-
Subqueries in the SELECT Clause
SELECT invoice_id, invoice_total, (SELECT AVG(invoice_total) FROM invoices) AS invoice_average, invoice_total - (SELECT invoice_average) AS difference FROM invoices
-
Subqueries in the FROM Clause
SELECT * FROM ( -- SUB QUERY SELECT client_id, (SELECT AVG(invoice_total) FROM invoices) AS invoice_average, invoice_total - (SELECT invoice_average) AS difference FROM client c )
-
Numeric Functions
SELECT ROUND(5.73) -- 6 ROUND(5.73, 1) -- 5.7 TRUNCATE(5.73, 1) -- 5.7 -- REMOVE 3 CEILING(5.2) -- 6 FLOOR(5.2) -- 5 ABS(5.2) -- 5.2 ABS(-5.2) -- 5.2 RAND() -- random
-
String Functions
SELECT LOWER('SKY') -- sky UPPER('sky') -- SKY LTRIM(' sky') -- sky RTRIM('sky ') -- sky RTRIM(' sky ') -- sky RIGHT('Kindergaren', 6) -- garen LEFT('Kindergaren', 4) -- Kind SUBSTRING('Kindergaren', 3, 5) -- nderg SUBSTRING('Kindergaren', 3) -- ndergaren LOCATE('garten','Kindergaren') -- 7 -- starts at 0, return 0 if not found, return first match of char or chars REPLACE('Kindergaren','garen', 'X') -- KinderX CONCAT('first', 'last') -- firstlast CONCAT(first_name, '', last_name) AS full_name FROM customers
-
Date Functions in MySQL
SELECT NOW(), -- 2023-04-03 11:02:30 CURDATE(), -- 2023-04-03 CURTIME(), -- 11:02:30 YEAR(NOW()) -- 2023 HOUR(NOW()) -- 11 SECOND(NOW()) --30 DAYNAME(NOW()) -- Monday MONTHNAME(NOW()) -- April EXTRACT(YEAR FROM NOW()) -- 2023 SELECT * FROM orders WHERE YEAR(oreder_date) = YEAR(NOW())
-
Formatting Dates and Times
'2023-04-04' SELECT DATE_FORMAT(NOW(), '%M %D %Y %H:%i %p' )
-
Calculating Dates and Times
SELECT DATE_ADD(NOW(), INTERVAL 1 YEAR) SELECT DATE_ADD(NOW(), INTERVAL -1 YEAR) SELECT DATE_SUB(NOW(), INTERVAL 1 YEAR) SELECT DATEDIFF('2023-03-02', '2023-04-04') -- IN DAYS SELECT DATEDIFF('2019-03-02 09:00', '2023-04-04 08:00') -- IN DAYS SELECT TIME_TO_SEC('09:00') - TIME_TO_SEC('09:02') -- -120
-
The IFNULL and COALESCE Functions
SELECT order_id, IFNULL (shipper_id, 'Not Assigned') AS shipper FROM orders SELECT order_id, COALESCE(shipper_id, 'Not Assigned') AS shipper FROM orders
-
The IF Function
SELECT order_id, order_date, IF( YEAR(order_date) = YEAR(NOW()), --statement 'active', --true 'archived' --false ) AS frequency FROM orders
-
The CASE Operator
SELECT order_id, CASE WHEN YEAR(order_date) = YEAR(NOW()) THEN 'Active' WHEN YEAR(order_date) = YEAR(NOW()) - 1 THEN 'Last year' WHEN YEAR(order_date) < YEAR(NOW()) - 1 THEN 'Archived' ELSE 'FUTURE' END AS category FROM orders
-
Creating Views
- useful when using multiple query or subuery --> save it in the view
- view are created with query
- view dont store data,
- we can use view the same way as table
- save views in git source control
USE sql_invoicing; -- use CREATE VIEW sales_by_client AS -- create -- CREATE OR REPLACE VIEW .... SELECT -- query c.client_id, c.name, SUM(invoice_total) AS total FROM clients c JOIN invoices i USING(client_id) GROUP BY client_id, name
-
Altering or Dropping Views
DROP VIEW sales_by_client
-
Updatable Views
- if the view DOES NOT include the follwing, it is a updateable VIEW
- DISTINCT
- AGGREGATE FNS (MIN, MAX, SUM ...)
- GROUP BY / HAVINIG
- UNION
-- 1 DELETE FROM invoices_with_balance WHERE invoice_id = 1 -- 2 UPDATE invoices_with_balance SET due_date = DATE_ADD(due_date, INTERVAL 2 DAY) WHERE invoice_id = 1
- if the view DOES NOT include the follwing, it is a updateable VIEW
-
THE WITH OPTION CHECK Clause
- WHEN DELETE OR UPDATE VIEWS, SOME ROWS MAY DISPAIR, USE ... WITH CHECK OPTION
CREATE OR REPLACE VIEW view_name SELECT invoice_id, invoice_total, payment_total, invoice_total - payment_total AS balance FROM invoices WHERE (invoice_total - payment_total) > 0 -- SOME ROWS MAY DISPAIR, if we make this <= 0, the row dispair WITH CHECK OPTION -- prevent it from dispairing
-
Other Benefits of Views
- simplify queries
- reduce impact of changes
- restrict access to data
-
What are Stored Procedures
- seperate SQL code from application code
- stored procedure is a database object that contains SQL code
- calls Procedure code in application code
- benefits: ORGANIZE | FAST EXECUTION | DATA SECURITY
-
Creating a Stored Procedure
- DELIMITER change and restore
- create and wrap
- end statement
DELIMITER $$ CREATE PROCEDURE get_clients() BEGIN SELECT * FROM clients; END DELIMITER ;
-
Creating Procedures Using MySQLWorkbench
- let MySQLWorkbench do the syntax, we focus on the query.
- right click stored procedures > create
-
Dropping Stored Procedures
DROP PROCEDURE get_clients DROP PROCEDURE IF EXISTS get_clients
-
Parameters
- all params are required in sql
DELIMITER $$ CREATE PROCEDURE get_client_by_prov ( prov CHAR(2) ) BEGIN SELECT * FROM client c WHERE c.prov = prov END DELIMITER ;
-
Parameters with Default Value
DELIMITER $$ CREATE PROCEDURE get_client_by_prov ( prov CHAR(2) ) BEGIN IF prov IS NULL THEN SET state = 'CA'; END IF; SELECT * FROM client c WHERE c.prov = prov END $$ DELIMITER ;
DELIMITER $$ CREATE PROCEDURE get_client_by_prov ( prov CHAR(2) ) BEGIN IF prov IS NULL THEN SELECT * FROM clients; ELSE SELECT * FROM client c WHERE c.prov = prov; END IF; END $$ DELIMITER ;
DELIMITER $$ CREATE PROCEDURE get_client_by_prov ( prov CHAR(2) ) BEGIN SELECT * FROM client c WHERE c.prov = IFNULL(prov, c.prov) END $$ DELIMITER ;
-
Parameter Validation
- keep validtion minimal, perform validtion in application code instead
CREATE PROCEDURE make_payment( invoice_id INT, payment_amount DECIMAL(9,2), payment_date DATE ) BEGIN IF payment_amount <= 0 THEN -- validation SIGNAL SQLSTATE '22003' -- error status SET MESSAGE_TEXT = 'Invalid payment amount'; -- error message END IF ; -- end validation UPDATE invoices i SET i. payment_total = payment_amount, i.payment_date = payment_date, WHERE i.invoice_id = invoice_id; END
-
Output Parameters
- avoid using output params if possible.
- Session based
CREATE PROCEDURE get_unpaid_for_client ( client_id INT, OUT invoices_count INT -- delcare output params OUT invoices_total DECIMAL(9,2) ) BEGIN SELECT COUNT(*), SUM(invoice_total) INTO invoices_count, invoices_total -- select values into out params FROM invoices i WHERE i.client_id = client_id AND payment_total = 0 END
-
Variables
- user variable : SET @invoices_count = 0
- Session based
- local variable : DECLARE risk_factor DECIMAL(9,2) DEFAULT 0
- procedure based
CREATE PROCEDURE get_risk_factor ( BEGIN DECLARE risk_factor DECIMAL(9,2) DEFAULT 0; --DECLARE LOCAL VAR DECLARE invoices_total DECIMAL(9,2); DECLARE invoices_count INT; SELECT COUNT(*), SUM(invoices_total) INTO invoices_count, invoices_total -- SLECT VALUE INTO PARAMS FROM invoices; SET risk_factor = invoices_total / invoices_count * 5; -- SET VALUE SELECT risk_factor; -- SELECT LOCAL VAR END )
- user variable : SET @invoices_count = 0
-
Functions
- functions are similar to procedures
- function can only return a single value
- function must return a value
- function must has at least one attribute
CREATE FUNCTION get_riskfactor_for_client ( client_id INT ) RETURN INTEGER -- Type of value, this function returns READS SQL DATA -- DETERMINISTIC -- PURE -- MODIFIES SQL DATA BEGIN DECLARE risk_factor DECIMAL(9,2) DEFAULT 0; --DECLARE LOCAL VAR DECLARE invoices_total DECIMAL(9,2); DECLARE invoices_count INT; SELECT COUNT(*), SUM(invoices_total) INTO invoices_count, invoices_total -- SLECT VALUE INTO PARAMS FROM invoices i; WHERE i.client_iid = client_id SET risk_factor = invoices_total / invoices_count * 5; -- SET VALUE RETURN IFNULL(risk_factor, 0); END
- use it eleswhere
SELECT client_id, get_riskfactor_for_client(client_id) AS risk_factor FROM clients
DROP FUNCTION IF EXISTS get_riskfactor_for_client;
-
Triggers
- DEF: SQL CMD that gets executed upon insert, update or delete
-
Viewing Triggers
-
Dropping Triggers
-
Using Triggers for Auditing
-
Events
- DEF: SQL CMD that gets executed according to a SCHEDULE
-
Viewing, Dropping and Altering Events
-
Transactions
-
Creating Transactions
-
Concurrency and Locking
-
Concurrency Problems
-
Transaction Isolation Levels
-
READ UNCOMMITTED Isolation Level
-
READ COMMITTED Isolation Level
-
REPEATABLE READ Isolation Level
-
SERIALIZABLE Isolation Level
-
Deadlocks
- String Types
- Integer Types
- Fixed-point and Floating-point Types
- Boolean Types
- Enum and Set Types
- Date and Time Types
- Blob Types
- JSON Type
- Introduction
- Data Modelling
- Conceptual Models
- Logical Models
- Physical Models
- Primary Keys
- Foreign Keys
- Foreign Key Constraints
- Normalization
- 1NF- First Normal Form
- Link Tables
- 2NF- Second Normal Form
- 3NF- Third Normal Form
- My Pragmatic Advice
- Don't Model the Universe
- Forward Engineering a Model
- Synchronizing a Model with a Database
- Reverse Engineering a Database
- Project- Flight Booking System
- Solution- Conceptual Model
- Solution- Logical Model
- Project - Video Rental Application
- Solution- Conceptual Model
- Solution- Logical Model
- Creating and Dropping Databases
- Creating Tables
- Altering Tables
- Creating Relationships
- Altering Primary and Foreign Key Constraints
- Character Sets and Collations
- Storage Engines
1- Introduction 2- Indexes 3- Creating Indexes 4- Viewing Indexes 5- Prefix Indexes 6- Full-text Indexes 7- Composite Indexes 8- Order of Columns in Composite Indexes 9- When Indexes are Ignored 10- Using Indexes for Sorting 11- Covering Indexese 12- Index Maintenance
- Introduction
- Creating a User
- Viewing Users
- Dropping Users
- Changing Passwords
- Granting Privileges
- Viewing Privileges
- Revoking Privileges