Data Retrieval
Data Retrieval
-- We use comments to add notes to our code.This is a comment and it won’t get
executed.
USE sql_store;
-- if you type the above code, you will see a red squeky line underlying the SELECT
statement and if you hover your mouse on it, you will see it is showing syntax
error.
-- you need to terminate the USE statement with a semi-colon and the error will
disappear
USE sql_store;
-- we can add the where clause to make our query even more specific
-- as we keep expanding our code, the it becomes longer and longer. it is therefore
advisable to put breaks in your code to make it more readable
-- executing the above will not give us much info since we have only selected one
customer.
-- let's delete the WHERE clause and run again.
-- NOTE:
-- the three clauses used above must follow the exact order. you cannot interchange
their positions
-- else SQL will give syntax error.
-- the clauses are optional
-- you can also break the SELECT clause for more readability
SELECT
last_name,
first_name,
points,
points + 20
FROM
customers;
-- NB: the order of operators in here follows the same logic as in math (i.e
BODMAS)
-- it is better to use brackets to tell SQL the order of operation you are looking
for
SELECT
last_name,
first_name,
points,
(points * 20) - 5
FROM
customers;
-- if you notice, the column names are given as the operation we performed
which does not look good or even make sense.
-- we can aliase it or rename it to a more meaningful one
SELECT
last_name,
first_name,
points,
(points * 20)- 5, -- am leaving this here so you see the difference
(points * 20) - 5 AS discount_factor -- if want to have a space in the colum
name, you will need to add quotes e.g. 'discount factor'
FROM
customers;
-- we can use the asterisks to extract all columns instead of being specific
SELECT *,
(points * 20)- 5, -- am leaving this here so you see the difference
(points * 20) - 5 AS discount_factor -- if want to have a space in the colum
name, you will need to add quotes e.g. 'discount factor'
FROM
customers;
SELECT state
FROM customers;
-- let's say we want to only unique states where our customers are located.
-- EXERCISE
-- SAMPLE SOLUTION:
SELECT
name,
unit_price,
unit_price - (unit_price * 0.05) AS 'discounted price'
FROM products;
SELECT
name,
unit_price,
round (unit_price - (unit_price * 0.05), 2) AS 'discounted price'
FROM products;
SELECT *
FROM customers
WHERE points > 1000;
-- >
-- >=
-- <
-- <=
-- =
-- != or <>
SELECT *
FROM customers
WHERE points = 947;
-- ---
SELECT *
FROM customers
WHERE state = 'VA';
-- ---
SELECT *
FROM customers
WHERE birth_date > '1980-01-02'; -- in SQL we need to add quotes to dates
-- EXERCISE
-- Write an SQL query to return the orders that were made before 2019
-- SAMPLE SOLUTION:
SELECT *
FROM orders
WHERE order_date < '2019-01-01';
-- -- AND
SELECT *
FROM customers
WHERE birth_date > '1980-01-02' AND points >= 1500;
-- -- OR
SELECT *
FROM customers
WHERE birth_date > '1980-01-02' OR points >= 1500;
SELECT *
FROM customers
WHERE birth_date > '1990-01-02' OR points >= 1500 AND state = 'VA';
-- NB: SQL gives preference to the AND operator before the OR operator
-- so in our example, the AND part first gets executed before the OR part
-- NOT
-- we can use the NOT operator to perform exclusive selections.
-- that is, to select anything else apart from the condition
SELECT *
FROM customers
WHERE NOT (birth_date > '1990-01-02' OR points > 1500 );
-- now we can see that if we appy the NOT to negate the items in the bracket we get
the same results as shown below:
SELECT *
FROM customers
WHERE birth_date <= '1990-01-02' AND points <= 1500 ;
-- EXERCISE
-- SAMPLE SOLUTION:
SELECT
order_id, product_id, quantity, unit_price,
unit_price* quantity AS total_price
FROM order_items
WHERE order_id = 6 AND unit_price * quantity > 20;
-- IN
-- let's say we waant to locate all the customers that are located in say Florida
or Virginia, etc
SELECT *
FROM customers
WHERE state = 'VA' OR state = 'FL' OR state = 'GA';
-- the above code works fine but it becomes very tedious to write especially if
your dataset is huge
-- you want to be smart and write the following:
SELECT *
FROM customers
WHERE state = 'FL' OR 'VA' OR 'GA'; -- but this will not give you what you want.
SELECT *
FROM customers
WHERE state IN ( 'FL' , 'VA' ,'GA'); -- the order does not matter
-- we can also use the NOT operator to select data that are not in the specified
states
SELECT *
FROM customers
WHERE state NOT IN ( 'FL' , 'VA' ,'GA'); -- the order does not matter
-- EXERCISE
SELECT *
FROM products
WHERE quantity_in_stock IN (49, 38, 72 ); -- the order does not matter
-- BETWEEN
-- let's say we want to find out all the customers with points greater than or
equal 1000 but less than or equal to 3000
-- we can write something like the following:
SELECT *
FROM customers
WHERE points >= 1000 AND points <= 3000;
-- we can rather use the BETWEEN operator to make our code shorter and cleaner:
SELECT *
FROM customers
WHERE points BETWEEN 1000 AND 3000;
-- EXERCISE
-- SAMPLE SOLUTION:
SELECT *
FROM customers
WHERE birth_date BETWEEN '01/01/1990' AND '01/01/2000'; -- this give me nothing
because that is not how our date is formatted
--
SELECT *
FROM customers
WHERE birth_date BETWEEN '1990/01/01' AND '2000/01/01';
-- --LIKE
-- let's say we want only customers whose last name starts with b
SELECT *
FROM customers
WHERE last_name LIKE 'b%'; -- we use the % sign to indicate any number of
characters can come after b, but first it should start with b, it doesn't matter if
uppercase or lower case b
-- we can also get customers whose last name starts with say 'brush'
SELECT *
FROM customers
WHERE last_name LIKE 'brush%';
-- -- let's say we want only customers whose last name contains 'b'
SELECT *
FROM customers
WHERE last_name LIKE '%b%';
-- let's say we want only customers whose first name ends with 't'
SELECT *
FROM customers
WHERE first_name LIKE '%y';
-- we can also use the underscore _ to get exactly the number of characters long we
want
SELECT *
FROM customers
WHERE last_name LIKE '_y'; -- meaning we want customers whose last name is exactly
2 characters and ends with 'y'
--
SELECT *
FROM customers
WHERE last_name LIKE '_____y' ;-- meaning we want customers whose last name is
exactly 5 characters and ends with 'y'
--
SELECT *
FROM customers
WHERE last_name LIKE 'b____y' ;-- meaning we want customers whose last name starts
with 'b' and has exactly 4 characters in between and also ends with 'y'
--
-- EXERCISE
-- SAMPLE SOLUTION:
-- 1. addresses contain TRAIL or AVENUE
SELECT *
FROM customers
WHERE address LIKE '%trail%' OR
address LIKE '%avenue%';
SELECT *
FROM customers
WHERE phone LIKE '%9';
-- we can also add the NOT operator to selector all customers whose phone number
does not end with 9
SELECT *
FROM customers
WHERE phone NOT LIKE '%9';
-- REGEXP
-- let's say we want customers who have the word 'field' in their last name
SELECT *
FROM customers
WHERE last_name LIKE '%field%' ;
-- we also have an alternative way of doing this using regular expression (REGEXP)
-- REGEXP helps us to do even more complex queries
SELECT *
FROM customers
WHERE last_name REGEXP 'field' ; -- same results as above
SELECT *
FROM customers
WHERE last_name REGEXP '^field' ; -- we do not have any name that start with
'field' so we won't get any results for this.
SELECT *
FROM customers
WHERE last_name REGEXP 'field$' ; -- showing customers with 'field at the end
-- let's we want to search for multiple words, we can use the pipe sign '|'
SELECT *
FROM customers
WHERE last_name REGEXP 'field|mac' ; -- make sure there no spaces included
--
SELECT *
FROM customers
WHERE last_name REGEXP 'field|mac|rose' ; -- make sure there no spaces included
-- let's say want all custmers whose have 'e' in their last name, and before the
letter 'e', there should be
SELECT *
FROM customers
WHERE last_name REGEXP '[gim]e' ; -- it matches with any customer have 'ge', 'ie',
or 'me' in their last name
-- --
SELECT *
FROM customers
WHERE last_name REGEXP 'e[gim]' ; -- it matches with any customer have 'eg', 'ei',
or 'em' in their last name
SELECT *
FROM customers
WHERE last_name REGEXP '[a-h]e' ;
-- summary of REGEXP:
-- ^ for beginning of a string
-- $ to indicate end of a string
-- | logical or
-- [a,b,c,d] to indicate combination of characters before or after a letter
-- [a-m] to indicate combination of characters before or after a letter
-- EXERCISE
-- SOLUTION
-- first names are ELKA or AMBUR
SELECT *
FROM customers
WHERE first_name REGEXP 'elka|ambur';
SELECT *
FROM customers
WHERE last_name REGEXP 'ey$|on$';
SELECT *
FROM customers
WHERE last_name REGEXP '^my|se';
SELECT *
FROM customers
WHERE last_name REGEXP 'b[r|u]';
-- alternatively
SELECT *
FROM customers
WHERE last_name REGEXP 'br|bu';
-- IS NULL operator
-- sometimes you want to know if there are records that are not available and the
IS NULL operator comes in handy.
SELECT *
FROM customers; -- we can observe that customer no. 5 does not have phone number
-- let's say we want to find all the customers who do not have phone
SELECT *
FROM customers
WHERE phone IS NULL;
-- we can also use the IS NOT NULL to do the opposite
SELECT *
FROM customers
WHERE phone IS NOT NULL;
-- EXERCISES
-- get all the orders that are not shipped
-- SOLUTION
-- if you look into the orders table you will realise that there are lots of
shipped_date and shipper_id
SELECT *
FROM orders
WHERE shipper_id IS NULL;
-- alternatively
SELECT *
FROM orders
WHERE shipped_date IS NULL;
SELECT *
FROM customers;
--
SELECT *
FROM customers
ORDER BY first_name;
SELECT *
FROM customers
LIMIT 5;
-- we can also set an offset to for conditional selection. e.g skipping the first 5
items and selecting the last 3 items
SELECT *
FROM customers
LIMIT 5,3;
-- EXERCISE
-- get the top 3 loyal customers
SELECT *
FROM customers
ORDER BY points DESC
LIMIT 3;
-- let's say we want to select the orders from the orders table and instead of
showing the orderID, we want to show the particular customer that ordered the item
SELECT *
FROM orders
JOIN customers -- we can add INNER but that is optional
ON orders.customer_id = customers.customer_id;
-- we can see from the output that the orders table comes first and then followed
by the customers table
-- we can simplify this further:
-- if we try to add the customer ID we will get an error, that is because SQL is
not sure which table to select customer ID from. We can get around that by
specifying that as follows:
-- we can also give an alias to the names: orders and customers to avoid them being
repeated often.alter
-- NOTE: once you create an alias, you need to use that alias throughout , you
can't alias orders to 'o' and still want to use the name orders instead of the
alias'o'
-- EXERCISE
-- write a query to join the order_items table with the products table and return
-- product ID
-- product name
-- quantity
-- unit price
-- NB: use alias to simplify your code.alter
-- SOLUTION
SELECT *
FROM order_items oi
JOIN sql_inventory.products p
ON oi.product_id = p.product_id
-- Important note:
-- you need to prefix the table that is not in the current database, e.g. in the
above code, we run it in the sql_store database so we had to prefix the products
with sql_inventory.products since we selected product table that is not in the
active database(sql_store)
USE sql_hr;
SELECT *
FROM employees e
JOIN employees m -- we are using a different letter here since it is the same
table, and we prefer m to represent managers
ON e.reports_to = m.employee_id;
-- we can observe that we have only one manager that everyone reports to and that
manager does not report to anyone so his report to column is having null.
-- we can simplify the table:
SELECT
e.employee_id,
e.first_name,
m.first_name
FROM employees e
JOIN employees m
ON e.reports_to = m.employee_id
-- if we look at the orders table in the sql_store databse, we notice that we have
status column there,
-- however, the meaning of those status can be found in the order_status table.
-- now we want to write a query to join the orders table with two other tables.
i.e. the customers table and order_status table
-- we want the order_id, order_date, first_name, last_name, status
USE sql_store;
SELECT *
FROM orders o
JOIN customers c
ON o.customer_id = c.customer_id
JOIN order_statuses os
ON o.status = os.order_status_id
-- nb: in the real world you can even join 10 tables so this is not uncommon, it
happens.
-- now we get information from the orders table followed by info from the customers
table and lastly from the order status table
-- EXERCISE
-- write a query and join the payments table with the payment_methods table as well
as the clients
-- producce a report that shows the payment with more details such as the name
of the client, and the payment method.
-- nb: the tables are found in the sql_invoicing database
-- SOLUTION
USE sql_invoicing;
SELECT *
FROM payments p
JOIN clients c
ON p.client_id = c.client_id
JOIN payment_methods pm
ON p.payment_method = pm.payment_method_id;
-- you can modify the code by selecting only the necessary columns
SELECT
p.date,
p.invoice_id,
p.amount,
c.name,
pm.name AS payment_method
FROM payments p
JOIN clients c
ON p.client_id = c.client_id
JOIN payment_methods pm
ON p.payment_method = pm.payment_method_id;
SELECT
c.customer_id,
c.first_name,
o.order_id
FROM customers c
JOIN orders o
ON c.customer_id = o.customer_id
ORDER BY c.customer_id
-- we can see from the output that we have results for only customers that have
orders, meanwhile if you take a look at the customer table, you will realise there
other customers as well.->
-- we want to see all the customers, whether they have an order or not, that's when
we need an outer join
SELECT
c.customer_id,
c.first_name,
o.order_id
FROM customers c
LEFT JOIN orders o -- left join returns all the customers whether they have orders
or not(i.e whether the ON condition is true or not)
ON c.customer_id = o.customer_id
ORDER BY c.customer_id
-- we can see from the output that all the customers are returned including those
with no orders
-- RIGHT JOIN
SELECT
c.customer_id,
c.first_name,
o.order_id
FROM customers c
RIGHT JOIN orders o -- RIGHT join returns all the orders (i.e whether the ON
condition is true or not)
ON c.customer_id = o.customer_id
ORDER BY c.customer_id
-- if you want to see all the cusomers, you can swap the position of the tables
SELECT
c.customer_id,
c.first_name,
o.order_id
FROM orders o
RIGHT JOIN customers c -- you can add the keyword OUTER but that is optional
ORDER BY c.customer_id
-- EXERCISE
-- write a query that produces a table with the following results:
-- product_id, name(i.e. name of product), quantity(you can get that
from the order items table)
-- return the product even if it has never been ordered
-- SOLUTION
USE sql_store;
SELECT *
FROM products p
LEFT JOIN order_items oi
ON p.product_id = oi.product_id
SELECT
p.product_id,
p.name,
oi.quantity
FROM products p
LEFT JOIN order_items oi
ON p.product_id = oi.product_id;
SELECT
o.order_id,
c.first_name
FROM orders o
JOIN customers c
-- ON o.customer_id = c.customer_id
USING (customer_id); -- we get the same results as above ON condition
SELECT
o.order_id,
c.first_name,
sh.name AS Shipper
FROM orders o
JOIN customers c
USING (customer_id)
LEFT JOIN shippers sh
USING (shipper_id);
-- we have all the customers alongside with their shippers and order id
-- NB: the USING keyword only works if the column name that you want to join are
same. so you cannot join say order_id with order_status_id
-- EXERCISE
-- using the sql_invoicing database, write a query that returns:
-- date
-- amount
-- client
-- name (i.e name of payment menthod)
-- SOLUTION
USE sql_invoicing;
SELECT
p.date,
p.amount,
c.name AS client,
pm.name AS payment_method
FROM payments p
JOIN clients c USING (client_id)
JOIN payment_methods pm
ON p.payment_method = payment_method_id