SQL Tutorial for Beginners in 3.5 hours
Follow the below 18 steps to learn SQL from scratch.
Download all the scripts used from here
1) Install the PostgreSQL database & PgAdmin IDE tool from here
2) Connect to the Postgres server and create a new database named “demo”.
select * from information_schema.tables; -- Understand what is a relational database? Create database demo;
3) Login to the demo database through the PgAdmin tool.
4) Create a simple table
create table products ( id int, name varchar(50), price float, release_date date ); -- What is a table, column and rows. -- What are data types?
5) Load data
insert into products (id,name,price,release_date) values(1,'iPhone 15',900,'22-08-2023'); insert into products (id,name,price,release_date) values(1,'Macbook Pro',2000,'10-02-2021'); insert into products (id,name,price,release_date) values(1,'AirPods',400,'15-04-2022'); insert into products (id,name,price,release_date) values(1,'iPhone 15',900,to_date('22-08-2023','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(1,'Macbook Pro',2000,to_date('10-02-2021','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(1,'AirPods',400,to_date('15-04-2022','dd-mm-yyyy'));
6) Read data
-- fetch all columns and rows from table select * from products; -- fetch selected rows but all columns select * from products where price > 500; -- fetch only the product names select name from products; -- fetch name and display column name as Product_name select name as Product_name from products; -- give an alias to table select name as Product_name from products p; select p.name as Product_name from products p; -- fetch name of products which were released in 2023 select name from products where to_char(release_date, 'yyyy') = '2023' select name from products where extract(year from release_date) = '2023' -- get total no of products select count(*) from products -- get the average and total price of all products select avg(price) from products; select avg(price) as avg_cost, sum(price) as total_sum from products;
7) Modify data
-- change the price of iPhone to 1000 update products set price = 1000 where id = 1; -- this will update wrong records update products set price = 1000 where name = 'iPhone 15'; update products set price = 1000 where name like 'iPhone%'; -- this would update any name with "iPhone"
8) Remove data
-- remove products costing over 1000 delete from products where price > 1000; -- remove all products other than iPhone delete from products where name not like 'iPhone%'; -- remove all products delete from products; truncate table products; -- this is a DDL command
9) Create a backup table
-- create a backup table of products with same structure and data create table products_bkp as select * from products; -- create a empty backup table with only the structure from products table create table products_bkp as select * from products where 1=2;
10) Remove table
drop table products; drop table if exists products;
11) Modify table
alter table products_bkp rename to products; alter table products rename column name to product_name; -- modify the data type of price to integer alter table products alter column price type int;
12) Primary key constraint
create table products ( id int primary key, name varchar(50), price float, release_date date ); create table products ( id int, name varchar(50), price float, release_date date, constraint pk_prd primary key(id) ); create table products ( id int, name varchar(50), price float, release_date date, constraint pk_prd primary key(id,name) /* allows to create a primary key with multiple columns (also referred to as composite primary key) */ );
13) Load data with constraints & Identity column
insert into products (id,name,price,release_date) values(1,'iPhone 15',900,to_date('22-08-2023','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(1,'Macbook Pro',2000,to_date('10-02-2021','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(1,'AirPods',400,to_date('15-04-2022','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(1,'iPhone 15',900,to_date('22-08-2023','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(2,'Macbook Pro',2000,to_date('10-02-2021','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(3,'AirPods',400,to_date('15-04-2022','dd-mm-yyyy')); drop table products; create table products ( id int generated always as identity primary key, name varchar(50) , price float, release_date date ); insert into products (id,name,price,release_date) values(default,'iPhone 15',900,to_date('22-08-2023','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(default,'Macbook Pro',2000,to_date('10-02-2021','dd-mm-yyyy')); insert into products (id,name,price,release_date) values(default,'AirPods',400,to_date('15-04-2022','dd-mm-yyyy'));
14) Create a Sales Order dataset
drop table if exists products; create table products ( id int generated always as identity primary key, name varchar(100), price float, release_date date ); insert into products values(default,'iPhone 15', 800, to_date('22-08-2023','dd-mm-yyyy')); insert into products values(default,'Macbook Pro', 2100, to_date('12-10-2022','dd-mm-yyyy')); insert into products values(default,'Apple Watch 9', 550, to_date('04-09-2022','dd-mm-yyyy')); insert into products values(default,'iPad', 400, to_date('25-08-2020','dd-mm-yyyy')); insert into products values(default,'AirPods', 420, to_date('30-03-2024','dd-mm-yyyy')); drop table if exists customers; create table customers ( id int generated always as identity primary key, name varchar(100), email varchar(30) ); insert into customers values(default,'Meghan Harley', 'mharley@demo.com'); insert into customers values(default,'Rosa Chan', 'rchan@demo.com'); insert into customers values(default,'Logan Short', 'lshort@demo.com'); insert into customers values(default,'Zaria Duke', 'zduke@demo.com'); drop table if exists employees; create table employees ( id int generated always as identity primary key, name varchar(100) ); insert into employees values(default,'Nina Kumari'); insert into employees values(default,'Abrar Khan'); insert into employees values(default,'Irene Costa'); drop table if exists sales_order; create table sales_order ( order_id int generated always as identity primary key, order_date date, quantity int, prod_id int references products(id), status varchar(20), customer_id int references customers(id), emp_id int, constraint fk_so_emp foreign key (emp_id) references employees(id) ); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),2,1,'Completed',1,1); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),3,1,'Pending',2,2); insert into sales_order values(default,to_date('02-01-2024','dd-mm-yyyy'),3,2,'Completed',3,2); insert into sales_order values(default,to_date('03-01-2024','dd-mm-yyyy'),3,3,'Completed',3,2); insert into sales_order values(default,to_date('04-01-2024','dd-mm-yyyy'),1,1,'Completed',3,2); insert into sales_order values(default,to_date('04-01-2024','dd-mm-yyyy'),1,3,'completed',2,1); insert into sales_order values(default,to_date('04-01-2024','dd-mm-yyyy'),1,2,'On Hold',2,1); insert into sales_order values(default,to_date('05-01-2024','dd-mm-yyyy'),4,2,'Rejected',1,2); insert into sales_order values(default,to_date('06-01-2024','dd-mm-yyyy'),5,5,'Completed',1,2); insert into sales_order values(default,to_date('06-01-2024','dd-mm-yyyy'),1,1,'Cancelled',1,1); SELECT * FROM products; SELECT * FROM customers; SELECT * FROM employees; SELECT * FROM sales_order;
15) Foreign key constraint
-- Before foreign key constraint drop table if exists sales_order; create table sales_order ( order_id int generated always as identity primary key, order_date date, quantity int, prod_id int , status varchar(20), customer_id int , emp_id int ); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),2,1,'Completed',1,1); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),1,10,'Completed',11,100); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),1,15,'Completed',11,100); -- After applying foreign key drop table if exists sales_order; create table sales_order ( order_id int generated always as identity primary key, order_date date, quantity int, prod_id int references products(id), status varchar(20), customer_id int , emp_id int, constraint fk_so_cust foreign key(customer_id) references customers(id), constraint fk_so_emp foreign key(emp_id) references employees(id) ); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),2,1,'Completed',1,1); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),1,5,'Completed',4,3); insert into sales_order values(default,to_date('01-01-2024','dd-mm-yyyy'),1,3,'Completed',2,1);
16) Dropping table with constraints
-- cannot drop a table which is being referred(used) by other table. drop table if exists products; drop table if exists customers; drop table if exists employees; drop table if exists sales_order; -- First drop the child table which refers other tables drop table if exists sales_order; drop table if exists products; drop table if exists customers; drop table if exists employees;
17) Case Study - 20 SQL Queries
-- 1. Identify the total no of products sold select sum(quantity) as total_products from sales_order; -- 2. Other than Completed, display the available delivery status's select distinct status from sales_order where status <> 'Completed'; select distinct status from sales_order where status not in ('Completed', 'completed'); select distinct status from sales_order where lower(status) <> 'completed'; select distinct status from sales_order where upper(status) <> 'COMPLETED'; -- 3. Display the order id, order_date and product_name for all the completed orders. select so.order_id, so.order_date, p.name from sales_order so join products p on p.id=so.prod_id where lower(so.status) = 'completed'; -- 4. Sort the above query to show the earliest orders at the top. Also, display the customer who purchased these orders. select so.order_id, so.order_date, p.name as product, c.name as customer from sales_order so join products p on p.id=so.prod_id join customers c on c.id = so.customer_id where lower(so.status) = 'completed' order by so.order_date; -- 5. Display the total no of orders corresponding to each delivery status select status, count(*) as tot_orders from sales_order so group by status; -- 6. How many orders are still not completed for orders purchasing more than 1 item? select count(status) as not_completed_orders from sales_order so where quantity > 1 and lower(status) <> 'completed'; -- 7. Find the total number of orders corresponding to each delivery status by ignoring the case in the delivery status. The status with highest no of orders should be at the top. select status, count(*) as tot_orders from (select case when lower(status) = 'completed' then 'Completed' else status end as status from sales_order) sq group by status order by tot_orders desc; select upper(status) as status, count(*) as tot_orders from sales_order so group by upper(status) order by tot_orders desc; -- 8. Write a query to identify the total products purchased by each customer select c.name as customer, sum(quantity) as total_products from sales_order so join customers c on c.id = so.customer_id group by c.name; -- 9. Display the total sales and average sales done for each day. select order_date, sum(quantity*price) as total_sales , avg(quantity*p.price) as avg_sales from sales_order so join products p on p.id = so.prod_id group by order_date order by order_date; -- 10. Display the customer name, employee name, and total sale amount of all orders which are either on hold or pending. select c.name as customer, e.name as employee , sum(quantity*p.price) as total_sales from sales_order so join employees e on e.id = so.emp_id join customers c on c.id = so.customer_id join products p on p.id = so.prod_id where status in ('On Hold', 'Pending') group by c.name, e.name; -- 11. Fetch all the orders which were neither completed/pending or were handled by the employee Abrar. Display employee name and all details of order. select e.name as employee, so.* from sales_order so join employees e on e.id = so.emp_id where lower(e.name) like '%abrar%' or lower(status) not in ('completed', 'pending'); -- 12. Fetch the orders which cost more than 2000 but did not include the MacBook Pro. Print the total sale amount as well. select (so.quantity * p.price) as total_sale, so.* from sales_order so join products p on p.id = so.prod_id where prod_id not in (select id from products where name = 'Macbook Pro') and (so.quantity * p.price) > 2000; -- 13. Identify the customers who have not purchased any product yet. select * from customers where id not in (select distinct customer_id from sales_order); select c.* from customers c left join sales_order so on so.customer_id = c.id where so.order_id is null; select c.* from sales_order so right join customers c on so.customer_id = c.id where so.order_id is null; -- 14. Write a query to identify the total products purchased by each customer. Return all customers irrespective of whether they have made a purchase or not. Sort the result with the highest no of orders at the top. select c.name , coalesce(sum(quantity), 0) as tot_prod_purchased from sales_order so right join customers c on c.id = so.customer_id group by c.name order by tot_prod_purchased desc; -- 15. Corresponding to each employee, display the total sales they made of all the completed orders. Display total sales as 0 if an employee made no sales yet. select e.name as employee, coalesce(sum(p.price * so.quantity),0) as total_sale from sales_order so join products p on p.id = so.prod_id right join employees e on e.id = so.emp_id and lower(so.status) = 'completed' group by e.name order by total_sale desc; -- 16. Re-write the above query to display the total sales made by each employee corresponding to each customer. If an employee has not served a customer yet then display "-" under the customer. select e.name as employee, coalesce(c.name, '-') as customer , coalesce(sum(p.price * so.quantity),0) as total_sale from sales_order so join products p on p.id = so.prod_id join customers c on c.id = so.customer_id right join employees e on e.id = so.emp_id and lower(so.status) = 'completed' group by e.name, c.name order by total_sale desc; -- 17. Re-write the above query to display only those records where the total sales are above 1000 select e.name as employee, coalesce(c.name, '-') as customer , coalesce(sum(p.price * so.quantity),0) as total_sale from sales_order so join products p on p.id = so.prod_id join customers c on c.id = so.customer_id right join employees e on e.id = so.emp_id and lower(so.status) = 'completed' group by e.name, c.name having sum(p.price * so.quantity) > 1000 order by total_sale desc; -- 18. Identify employees who have served more than 2 customers. select e.name, count(distinct c.name) as total_customers from sales_order so join employees e on e.id = so.emp_id join customers c on c.id = so.customer_id group by e.name having count(distinct c.name) > 2; -- 19. Identify the customers who have purchased more than 5 products select c.name as customer, sum(quantity) as total_products_purchased from sales_order so join customers c on c.id = so.customer_id group by c.name having sum(quantity) > 5; -- 20. Identify customers whose average purchase cost exceeds the average sale of all the orders. select c.name as customer, avg(quantity * p.price) from sales_order so join customers c on c.id = so.customer_id join products p on p.id = so.prod_id group by c.name having avg(quantity * p.price) > (select avg(quantity * p.price) from sales_order so join products p on p.id = so.prod_id);
18) Next steps
a) Go to LeetCode (or any other SQL practice platforms) and practice solving all EASY SQL problems.
b) Learn the following concepts
i) Subqueries from here
ii) Window functions from here and here
iii) All types of JOINS from here and here
iv) CTE table (WITH clause) from here
c) After learning the above 4 concepts, you can start solving Medium level problems on LeetCode or other SQL practice platforms.