SQL Complete Practice Guide
SQL Complete Practice Guide
Group O — SUBQUERY
1. Find employees who earn more than the average salary (subquery
in WHERE).
2. Find the department(s) with the highest total salary (subquery +
aggregate).
3. Find employees who are NOT assigned to any project (subquery
with NOT IN).
4. Find the name of the employee with the maximum salary using a
subquery.
5. Find all projects whose budget is above the average budget of all
projects.
Group P — VIEW
1. Show each employee’s salary with a label: 'High' if salary > 90000,
'Medium' if 60000–90000, else 'Low'.
2. Show 'Active' or 'Inactive' text instead of TRUE/FALSE for
is_active using CASE.
3. Categorize projects as 'Completed', 'Ongoing' (NULL end_date), or
'Closed without info'.
4. Use CASE inside an ORDER BY to sort employees by department,
but put NULL department employees last.
5. Use CASE to assign a performance bonus % based on salary range,
then SELECT name, salary, bonus%.
Group U — TRIGGER
1. Select the current date, current time, and current datetime using
NOW(), CURRENT_DATE, CURRENT_TIME.
2. Extract the YEAR from each employee’s hire_date (EXTRACT or
DATE PART).
3. Find each employee’s age in service (AGE-style calculation) — how
many years since hire_date till today.
4. Find all employees hired in the year 2023 using EXTRACT/YEAR().
5. Find employees hired more than 5 years ago compared to today’s
date.
Group A
-- 1
CREATE DATABASE school_db;
-- 2
CREATE TABLE interns (
intern_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
joining_date DATETIME NOT NULL,
is_paid BOOLEAN DEFAULT FALSE,
stipend DECIMAL(10,2) CHECK (stipend >= 0)
);
-- 3
CREATE TABLE attendance (
attendance_id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT,
attendance_date DATE,
FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
);
-- 4: 'salary' column -> CHECK (salary > 0) ensures salary can never
be zero or negative.
-- 5
CREATE TABLE departments_history (
history_id INT AUTO_INCREMENT PRIMARY KEY,
department_id INT,
changed_on DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (department_id) REFERENCES
departments(department_id)
);
Group B
-- 1
ALTER TABLE employees ADD COLUMN bonus DECIMAL(10,2) DEFAULT 0;
-- 2
ALTER TABLE employees MODIFY COLUMN phone VARCHAR(20);
-- 3
ALTER TABLE departments RENAME COLUMN location TO office_location;
-- 4
ALTER TABLE employees DROP COLUMN bonus;
-- 5
ALTER TABLE employee_projects RENAME TO project_assignments;
Group C
-- 1
INSERT INTO departments (department_name, location) VALUES
('Marketing','Hyderabad');
-- 2
INSERT INTO employees
(first_name,last_name,email,hire_date,salary,department_id)
VALUES ('Tina','Roy','[Link]@[Link]','2023-09-01
09:00:00',45000,6);
-- 3
INSERT INTO employees
(first_name,last_name,email,hire_date,salary,department_id) VALUES
('Aditi','Shah','[Link]@[Link]','2023-10-01 09:00:00',47000,6),
('Farhan','Khan','[Link]@[Link]','2023-10-05
09:00:00',49000,6),
('Meera','Iyer','[Link]@[Link]','2023-10-10 09:00:00',46000,6);
-- 4
INSERT INTO projects
(project_name,start_date,end_date,budget,department_id)
VALUES ('Brand Campaign','2023-11-01',NULL,120000,6);
-- 5
INSERT INTO employee_projects
(employee_id,project_id,role_in_project,hours_worked) VALUES
(9,4,'Reviewer',30),
(12,5,'Tester',45);
Group D
-- 1
UPDATE employees SET salary = salary * 1.10 WHERE department_id = 1;
-- 2
UPDATE employees SET is_active = FALSE WHERE first_name='Neha' AND
last_name='Gupta';
-- 3
UPDATE projects SET budget = 250000 WHERE project_name = 'Website
Revamp';
-- 4
UPDATE employees SET manager_id = 1 WHERE manager_id IS NULL AND
employee_id <> 1;
-- 5
UPDATE employee_projects SET hours_worked = hours_worked + 10
WHERE employee_id = 2 AND project_id = 1;
Group E
-- 1
DELETE FROM employees WHERE first_name='Karan' AND
last_name='Malhotra';
-- 2
DELETE FROM employee_projects WHERE hours_worked = 0;
-- 3
DELETE FROM projects WHERE end_date IS NULL AND budget < 100000;
-- 4
DELETE FROM departments
WHERE department_id NOT IN (SELECT DISTINCT department_id FROM
employees WHERE department_id IS NOT NULL);
-- 5
DELETE FROM employee_projects
WHERE (employee_id, project_id) IN (
SELECT employee_id, project_id FROM (
SELECT employee_id, project_id, COUNT(*) c
FROM employee_projects GROUP BY employee_id, project_id HAVING c
> 1
) t
);
Group F
Group G
Group I
Group J
Group K
Group L
Group M
-- 1 INNER JOIN
SELECT e.first_name, d.department_name
FROM employees e INNER JOIN departments d ON e.department_id =
d.department_id;
-- 2 LEFT JOIN
SELECT e.first_name, d.department_name
FROM employees e LEFT JOIN departments d ON e.department_id =
d.department_id;
-- 3 RIGHT JOIN
SELECT d.department_name, e.first_name
FROM departments d RIGHT JOIN employees e ON d.department_id =
e.department_id;
-- (to truly see depts with 0 employees, swap to: employees e RIGHT
JOIN departments d ON e.department_id = d.department_id)
-- 4 SELF JOIN
SELECT e.first_name AS employee, m.first_name AS manager
FROM employees e LEFT JOIN employees m ON e.manager_id =
m.employee_id;
-- 5 CROSS JOIN
SELECT d.department_name, p.project_name
FROM departments d CROSS JOIN projects p;
-- Row count = (number of departments) x (number of projects)
Group N
-- 1
SELECT location FROM departments
UNION
SELECT x FROM (SELECT 'Pune' AS x UNION SELECT 'Goa') t;
-- 3
SELECT first_name FROM employees WHERE salary > 100000
UNION
SELECT first_name FROM employees WHERE department_id = 5;
Group O
-- 1
SELECT * FROM employees WHERE salary > (SELECT AVG(salary) FROM
employees);
-- 2
SELECT department_id, SUM(salary) AS total_sal FROM employees
GROUP BY department_id
HAVING SUM(salary) = (
SELECT MAX(total) FROM (SELECT SUM(salary) AS total FROM employees
GROUP BY department_id) t
);
-- 3
SELECT * FROM employees
WHERE employee_id NOT IN (SELECT employee_id FROM
employee_projects);
-- 4
SELECT first_name, last_name FROM employees
WHERE salary = (SELECT MAX(salary) FROM employees);
-- 5
SELECT * FROM projects WHERE budget > (SELECT AVG(budget) FROM
projects);
Group P
-- 1
CREATE VIEW active_employees_view AS
SELECT * FROM employees WHERE is_active = TRUE;
-- 2
CREATE VIEW dept_salary_summary AS
SELECT d.department_name, SUM([Link]) AS total_salary
FROM employees e JOIN departments d ON e.department_id =
d.department_id
GROUP BY d.department_name;
-- 3
SELECT * FROM dept_salary_summary ORDER BY total_salary DESC LIMIT
1;
-- 4
CREATE VIEW employee_manager_view AS
SELECT e.first_name AS employee_name, m.first_name AS manager_name
FROM employees e LEFT JOIN employees m ON e.manager_id =
m.employee_id;
-- 5
DROP VIEW active_employees_view;
Group Q
-- 1: Clustered index physically sorts/stores table data in index
order (only one per table,
-- usually the primary key). Non-clustered index is a separate
structure with pointers back
-- to the table rows — a table can have many of these.
-- 2
CREATE INDEX idx_emp_dept ON employees(department_id);
-- 3
CREATE UNIQUE INDEX idx_emp_email ON employees(email);
-- Redundant because UNIQUE constraint on email already auto-creates
a unique index internally.
-- 5
CREATE INDEX idx_proj_budget ON projects(budget);
Group R
-- 1
SELECT first_name, salary,
CASE
WHEN salary > 90000 THEN 'High'
WHEN salary BETWEEN 60000 AND 90000 THEN 'Medium'
ELSE 'Low'
END AS salary_band
FROM employees;
-- 2
SELECT first_name, CASE WHEN is_active THEN 'Active' ELSE 'Inactive'
END AS status
FROM employees;
-- 3
SELECT project_name,
CASE
WHEN end_date IS NOT NULL AND end_date < CURRENT_DATE THEN
'Completed'
WHEN end_date IS NULL THEN 'Ongoing'
ELSE 'Closed without info'
END AS project_status
FROM projects;
-- 4
SELECT * FROM employees
ORDER BY CASE WHEN department_id IS NULL THEN 1 ELSE 0 END,
department_id;
-- 5
SELECT first_name, salary,
CASE
WHEN salary > 90000 THEN 15
WHEN salary BETWEEN 60000 AND 90000 THEN 10
ELSE 5
END AS bonus_percent
FROM employees;
Group S
-- 1
CREATE FUNCTION get_annual_salary(monthly_salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
RETURN monthly_salary * 12;
-- 2
CREATE FUNCTION get_full_name(fname VARCHAR(50), lname VARCHAR(50))
RETURNS VARCHAR(101)
DETERMINISTIC
RETURN CONCAT(fname,' ',lname);
-- 3
CREATE FUNCTION years_of_service(hire_date DATETIME)
RETURNS INT
DETERMINISTIC
RETURN TIMESTAMPDIFF(YEAR, hire_date, CURDATE());
-- 4
SELECT first_name, get_annual_salary(salary) AS annual_salary FROM
employees;
-- 5
CREATE FUNCTION seniority_label(hire_date DATETIME)
RETURNS VARCHAR(10)
DETERMINISTIC
RETURN IF(TIMESTAMPDIFF(YEAR, hire_date, CURDATE()) > 5, 'Senior',
'Junior');
Group T
-- 1
DELIMITER //
CREATE PROCEDURE get_employees_by_dept(IN dept_id INT)
BEGIN
SELECT * FROM employees WHERE department_id = dept_id;
END //
DELIMITER ;
-- 2
DELIMITER //
CREATE PROCEDURE give_raise(IN emp_id INT, IN raise_amount
DECIMAL(10,2))
BEGIN
UPDATE employees SET salary = salary + raise_amount WHERE
employee_id = emp_id;
END //
DELIMITER ;
-- 3
DELIMITER //
CREATE PROCEDURE add_department(IN dept_name VARCHAR(50), IN
dept_location VARCHAR(50))
BEGIN
INSERT INTO departments (department_name, location) VALUES
(dept_name, dept_location);
END //
DELIMITER ;
-- 4
DELIMITER //
CREATE PROCEDURE deactivate_employee(IN emp_id INT)
BEGIN
UPDATE employees SET is_active = FALSE WHERE employee_id = emp_id;
END //
DELIMITER ;
-- 5
CALL get_employees_by_dept(1);
CALL give_raise(5, 2000);
CALL add_department('Legal','Kolkata');
CALL deactivate_employee(8);
Group U
-- 1
DELIMITER //
CREATE TRIGGER trg_check_salary
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
IF [Link] <= 0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Salary must be
positive';
END IF;
END //
DELIMITER ;
-- 2
CREATE TABLE salary_audit (
audit_id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT,
old_salary DECIMAL(10,2),
new_salary DECIMAL(10,2),
changed_on DATETIME DEFAULT CURRENT_TIMESTAMP
);
DELIMITER //
CREATE TRIGGER trg_salary_audit
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
IF [Link] <> [Link] THEN
INSERT INTO salary_audit (employee_id, old_salary, new_salary)
VALUES (OLD.employee_id, [Link], [Link]);
END IF;
END //
DELIMITER ;
-- 3
DELIMITER //
CREATE TRIGGER trg_default_active
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
IF NEW.is_active IS NULL THEN
SET NEW.is_active = TRUE;
END IF;
END //
DELIMITER ;
-- 4
DELIMITER //
CREATE TRIGGER trg_prevent_dept_delete
BEFORE DELETE ON departments
FOR EACH ROW
BEGIN
IF (SELECT COUNT(*) FROM employees WHERE department_id =
OLD.department_id) > 0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete
department with employees';
END IF;
END //
DELIMITER ;
-- 5
ALTER TABLE projects ADD COLUMN last_modified DATETIME;
DELIMITER //
CREATE TRIGGER trg_project_update_time
BEFORE UPDATE ON projects
FOR EACH ROW
BEGIN
SET NEW.last_modified = NOW();
END //
DELIMITER ;
Group V
SELECT employee_id,
TIMESTAMPDIFF(YEAR, hire_date, CURDATE()) AS years_in_service
FROM employees;
-- 3
Group W
-- 1
SELECT first_name, department_id, salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary
DESC) AS rn
FROM employees;
-- 2
SELECT first_name, salary,
RANK() OVER (ORDER BY salary DESC) AS company_rank
FROM employees;
-- 3
SELECT first_name, department_id, salary,
SUM(salary) OVER (PARTITION BY department_id) AS dept_total_salary
FROM employees;
-- 4
SELECT first_name, hire_date, salary,
LAG(salary) OVER (ORDER BY hire_date) AS prev_hired_salary
FROM employees;
-- 5
SELECT ep.employee_id, ep.project_id, ep.hours_worked,
AVG(ep.hours_worked) OVER (PARTITION BY ep.project_id) AS
avg_project_hours
FROM employee_projects ep;
Tip: Pehle Part 2 ke questions khud likh, run kar, fir Part 3 se match
kar. Agar koi query error de (especially trigger/procedure syntax —
MySQL vs PostgreSQL me thoda farak hota hai), bata dena, main us
specific RDBMS ke hisaab se fix kar dunga.