Syntax of PL/SQL Block
To ensure print statements appear on the screen, do :
SET SERVEROUTPUT ON
DECLARE
declaration statements ;
BEGIN
executable statements ;
EXCEPTION
exception handling statements ;
END ;
/
Example:
DECLARE
v_salary NUMBER;
v_bonus NUMBER := 1000;
BEGIN
SELECT salary
INTO v_salary
FROM employees
WHERE employee_id = 101;
IF v_salary > 5000 THEN
v_salary := v_salary + v_bonus;
END IF;
DBMS_OUTPUT.PUT_LINE('Updated salary : ' || v_salary);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An error occurred.');
END;
/
Example of a Function
Define a function that, given the name of a department, returns the count of the number of
instructors in that department.
Table Functions
CREATE FUNCTION instructor_of ( dept_name CHAR (20) )
RETURNS TABLE (
ID VARCHAR (5) ,
name VARCHAR (20) ,
dept_name VARCHAR (20) ,
salary NUMERIC (8 ,2) )
RETURN TABLE
( SELECT ID , name , dept_name , salary
FROM instructor
WHERE instructor . dept_name = dept_name ) ;
/
Running the function:
SELECT *
FROM TABLE ( instructor_of ('Music ') ) ;
Using Functions & Triggers
How to write functions that takes input parameters
CREATE OR REPLACE FUNCTION calculate_charge(myPlan IN
VARCHAR2,callseconds IN NUMBER) RETURN NUMBER
IS
TOTAL_BILL NUMBER(10,2):= 0;
CON_FEE NUMBER(6,2);
rate NUMBER(6,4);
BEGIN
SELECT CONNECTIONFEE,PRICEPERSECOND
INTO CON_FEE,rate
FROM PRICINGPLAN
WHERE CODE = myPlan;
TOTAL_BILL := CON_FEE + callseconds*rate;
RETURN TOTAL_BILL;
END;
/
CREATE OR REPLACE TRIGGER update_bill_after_call
AFTER INSERT ON PHONECALL
FOR EACH ROW
DECLARE
new_bill NUMBER(10,2);
cur_bill NUMBER(10,2);
myplan VARCHAR2(10);
BEGIN
SELECT AMOUNT
INTO cur_bill
FROM Bill
WHERE SSN = :NEW.SSN
FOR UPDATE; --to ensure no other process can modify BILL when
trigger is running
SELECT PLAN
INTO myplan
FROM MCUSTOMER
WHERE SSN = :NEW.SSN;
new_bill := cur_bill + calculate_charge(myplan,:NEW.Seconds);
UPDATE Bill
SET AMOUNT = new_bill
WHERE SSN = :NEW.SSN;
END;
/
Another Example (Uses Sequences)
CREATE SEQUENCE student_seq
START WITH 1
INCREMENT BY 1
MINVALUE 1
MAXVALUE 99
CYCLE; -- Reset the value back to 1
CREATE OR REPLACE FUNCTION Gen_ID (
p_Date_Of_Admission DATE,
p_Department CHAR,
p_Program CHAR,
p_Section CHAR
) RETURN VARCHAR2 IS
v_year VARCHAR2(2); -- To hold the last two digits of the year
v_seq VARCHAR2(2); -- To hold the sequence number as two digits
v_id VARCHAR2(10); -- The generated ID
BEGIN
v_year := TO_CHAR(p_Date_Of_Admission, 'YY');
v_seq := LPAD(student_seq.NEXTVAL, 2, '0');
v_id := v_year || '00' || p_Department || p_Program || p_Section ||
v_seq;
RETURN v_id;
END;
/
CREATE OR REPLACE TRIGGER trg_Before_Insert_IUTStudent
BEFORE INSERT ON IUTSTUDENT
FOR EACH ROW
BEGIN
:New.ID :=
GEN_ID(:NEW.Date_Of_Admission,:NEW.Department,:NEW.Program,:NEW.Secti
on);
END;
/
Procedure
Procedure examples with Cursors
Table Context
CREATE TABLE Library_Borrowing(
B_ID NUMBER,
Name VARCHAR2(100),
Book_Title VARCHAR2(100),
Borrow_Date DATE,
Due_Date DATE,
Returned_Date DATE
);
--Task 3.4
set SERVEROUTPUT on;
CREATE OR REPLACE PROCEDURE List_Overdue_Books
IS
CURSOR overdue_books_cur IS
SELECT Name,
Book_Title,
Due_Date,
TRUNC(SYSDATE - Due_Date) AS Days_Overdue
FROM Library_Borrowing
WHERE Returned_Date IS NULL;
BEGIN
FOR overdue_rec IN overdue_books_cur LOOP
IF overdue_rec.Days_Overdue < 0 THEN
--not overdue yet
DBMS_OUTPUT.PUT_LINE('Member: ' || overdue_rec.Name ||
', Book: ' || overdue_rec.Book_Title ||
', Due Date: ' || TO_CHAR(overdue_rec.Due_Date,
'DD-MON-YYYY') ||
', Overdue Soon..');
ELSE
--overdue
DBMS_OUTPUT.PUT_LINE('Member: ' || overdue_rec.Name ||
', Book: ' || overdue_rec.Book_Title ||
', Due Date: ' || TO_CHAR(overdue_rec.Due_Date,
'DD-MON-YYYY') ||
', Days Overdue: ' || overdue_rec.Days_Overdue);
END IF;
END LOOP;
END;
/
BEGIN
List_Overdue_Books;
End;
/
Loops
DECLARE
counter NUMBER := 1;
BEGIN
WHILE counter <= 5 LOOP
DBMS_OUTPUT . PUT_LINE ('Number : ' || counter ) ;
counter := counter + 1;
END LOOP ;
END ;
/
Print numbers from 1 to 5 using a FOR loop
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT . PUT_LINE ('Number : ' || i ) ;
END LOOP ;
END ;
/
Conditionals
DECLARE
num NUMBER := 7;
BEGIN
IF num = 0 THEN
DBMS_OUTPUT.PUT_LINE('The number is zero.');
ELSIF MOD(num, 2) = 0 THEN
DBMS_OUTPUT.PUT_LINE('The number is even.');
ELSE
DBMS_OUTPUT.PUT_LINE('The number is odd.');
END IF;
END;
/
Mathematical Functions
Triggers
Recursive Query Example
Implicit Cursors
DECLARE
total_rows NUMBER(2);
BEGIN
UPDATE emp
SET salary = salary + 500;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('No customers selected');
ELSIF SQL%FOUND THEN
total_rows := SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE(total_rows || ' customers selected');
END IF;
END;
/
Explicit Cursors
Explicit cursors are programmer-defined cursors for gaining more control over the context area.
An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created
on a SELECT statement which returns one or more rows.
DECLARE
c_id customers.id%TYPE;
c_name customers.name%TYPE;
c_addr customers.address%TYPE;
– Cursor created here
CURSOR c_customers IS
SELECT id, name, address FROM customers;
BEGIN
OPEN c_customers;
LOOP
FETCH c_customers INTO c_id, c_name, c_addr;
EXIT WHEN c_customers%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(c_id || ' ' || c_name || ' ' || c_addr);
END LOOP;
CLOSE c_customers;
END;
/
🟢 Explanation:
● Variable declarations:
1. c_id, c_name, and c_addr store the ID, name, and address from the
customers table.
● Cursor declaration (c_customers):
1. Declares a cursor to select id, name, and address from the customers table.
● Cursor operations:
1. OPEN c_customers: Opens the cursor to start fetching rows.
2. FETCH c_customers: Retrieves one row at a time into the declared variables.
3. EXIT WHEN c_customers%NOTFOUND: Exits the loop when no more rows are
found.
4. DBMS_OUTPUT.PUT_LINE: Prints each customer’s details.
5. CLOSE c_customers: Closes the cursor when done.
Cursor with loops
CREATE OR REPLACE FUNCTION determine_loan_scheme(my_user_id IN NUMBER) return
NUMBER
IS
total_amount NUMBER;
present_loan_scheme NUMBER := 0;
CURSOR c IS
SELECT *
From loan_type
ORDER by eligibility;
BEGIN
SELECT SUM(AMOUNT)
INTO total_amount
FROM TRANSACTIONS
WHERE user_id = my_user_id;
FOR r IN c LOOP
IF total_amount >= r.eligibility THEN
present_loan_scheme := r.scheme;
END IF;
END LOOP;
RETURN present_loan_scheme;
END;
/
Explicit Cursors that Accept Parameters
DECLARE
-- Declare a parameterized cursor
CURSOR c(job VARCHAR2, max_sal NUMBER) IS
SELECT last_name, first_name, (salary - max_sal) overpayment
FROM employees
WHERE job_id = job
AND salary > max_sal
ORDER BY salary;
-- Define a procedure to print overpaid employees
PROCEDURE print_overpaid IS
last_name_ employees.last_name%TYPE;
first_name_ employees.first_name%TYPE;
overpayment_ employees.salary%TYPE;
BEGIN
LOOP
FETCH c INTO last_name_, first_name_, overpayment_;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(last_name_ || ', ' || first_name_ ||
' (by ' || overpayment_ || ')');
END LOOP;
END print_overpaid;
BEGIN
-- Check for overpaid stock clerks
DBMS_OUTPUT.PUT_LINE('----------------------');
DBMS_OUTPUT.PUT_LINE('Overpaid Stock Clerks:');
DBMS_OUTPUT.PUT_LINE('----------------------');
OPEN c('ST_CLERK', 2500);
print_overpaid;
CLOSE c;
-- Check for overpaid sales representatives
DBMS_OUTPUT.PUT_LINE('-------------------------------');
DBMS_OUTPUT.PUT_LINE('Overpaid Sales Representatives:');
DBMS_OUTPUT.PUT_LINE('-------------------------------');
OPEN c('SA_REP', 10000);
print_overpaid;
CLOSE c;
END;
/
Rollup, Grouping,Extract example
Table Context
-- Create Tables
CREATE TABLE bus (
bus_id NUMBER PRIMARY KEY,
bus_model VARCHAR2(50),
manufacturer VARCHAR2(50)
);
CREATE TABLE driver (
driver_id NUMBER PRIMARY KEY,
driver_name VARCHAR2(100)
);
CREATE TABLE depot (
depot_id NUMBER PRIMARY KEY,
depot_name VARCHAR2(100),
city VARCHAR2(100)
);
CREATE TABLE time_dim (
time_id NUMBER PRIMARY KEY,
year NUMBER,
month NUMBER,
day NUMBER
);
CREATE TABLE trip (
trip_id NUMBER PRIMARY KEY,
segment_id NUMBER,
bus_id NUMBER,
driver_id NUMBER,
depot_id NUMBER,
trip_date DATE,
kilometers_traveled NUMBER,
fuel_consumption NUMBER,
passengers_count NUMBER,
trip_duration NUMBER,
FOREIGN KEY (bus_id) REFERENCES bus(bus_id),
FOREIGN KEY (driver_id) REFERENCES driver(driver_id),
FOREIGN KEY (depot_id) REFERENCES depot(depot_id)
);
1. Write a query to calculate the total kilometers traveled, fuel consumption, and passengers per
bus, grouped by year, month, and day.
2. Use the ROLLUP function to calculate subtotals and grand totals for kilometers traveled, fuel
consumption, and passengers, grouped by bus model, year, and month.
3. Use the CUBE function to generate all possible combinations of subtotals for kilometers
traveled, fuel consumption, and passengers, grouped by bus model, depot, and year.
4. Use the GROUPING function to distinguish between detailed rows and subtotal rows in a
ROLLUP query for bus model, year, and month.
5. Calculate the total kilometers traveled, fuel consumption, and passengers, grouped
by bus model, depot, and year.
--Task 1
SELECT Extract(Day FROM trip_date) as day,
Extract(MONTH FROM trip_date)as month,
Extract(YEAR FROM trip_date)as year,
SUM(kilometers_traveled) as total_KM,
SUM(fuel_consumption) as total_fuel_consumed,
(SUM(passengers_count)/COUNT(bus_id)) as avg_passengers
FROM Trip
Group By ROLLUP(Extract(Day FROM trip_date),
Extract(MONTH FROM trip_date)),
EXTRACT(YEAR FROM trip_date);
--Task 2
SELECT bus_model,
SUM(kilometers_traveled) as total_KM,
SUM(fuel_consumption) as total_fuel_consumed,
SUM(passengers_count) as total_passengers_carried,
Extract(MONTH FROM trip_date) as MONTH,
EXTRACT(YEAR FROM trip_date) as YEAR
FROM trip t
INNER JOIN bus b on b.bus_id = t.bus_id
GROUP BY ROLLUP(bus_model,
Extract(MONTH FROM trip_date),
EXTRACT(YEAR FROM trip_date));
--Task 3
SELECT
SUM(kilometers_traveled) as total_KM,
SUM(fuel_consumption) as total_fuel_consumed,
SUM(passengers_count) as total_passengers_carried,
EXTRACT(YEAR FROM trip_date) as YEAR,
bus_model,
depot_name
FROM trip t
INNER JOIN bus b on b.bus_id = t.bus_id
INNER JOIN depot d on d.depot_id = t.depot_id
GROUP BY CUBE(bus_model,
depot_name,
EXTRACT(YEAR FROM trip_date)
)
ORDER BY bus_model,depot_name,YEAR;
--Task 4
SELECT
SUM(kilometers_traveled) as total_KM,
SUM(fuel_consumption) as total_fuel_consumed,
SUM(passengers_count) as total_passengers_carried,
Extract(MONTH FROM trip_date) as month,
EXTRACT(YEAR FROM trip_date) as YEAR,
bus_model,
depot_name,
GROUPING(bus_model) as bus_model_grouping,
GROUPING(EXTRACT(YEAR FROM trip_date)) as year_grouping,
GROUPING(EXTRACT(MONTH FROM trip_date)) as month_grouping
FROM trip t
INNER JOIN bus b on b.bus_id = t.bus_id
INNER JOIN depot d on d.depot_id = t.depot_id
GROUP BY CUBE(bus_model,
depot_name,
EXTRACT(YEAR FROM trip_date),
Extract(MONTH FROM trip_date)
)
ORDER BY bus_model,depot_name,YEAR,MONTH;
--Task 5
SELECT
SUM(kilometers_traveled) as total_KM,
SUM(fuel_consumption) as total_fuel_consumed,
SUM(passengers_count) as total_passengers_carried,
EXTRACT(YEAR FROM trip_date) as YEAR,
bus_model,
depot_name
FROM trip t
INNER JOIN bus b on b.bus_id = t.bus_id
INNER JOIN depot d on d.depot_id = t.depot_id
GROUP BY bus_model,
depot_name,
EXTRACT(YEAR FROM trip_date)
ORDER BY bus_model,depot_name,YEAR;
Winter 2024 Triggers, Cursors, Recursive Queries and Advanced Aggregation Features Lab 2
4 Advanced Ranking Features
4.1 Ranking in SQL
Ranking functions in SQL allow you to assign a unique rank to each row within a result set based
on specified criteria. These functions are typically used in conjunction with the ORDER BY clause to
determine the order of ranking.
SELECT ID , RANK () OVER ( ORDER BY GPA DESC ) AS s_rank
FROM student_grades ;
This query assigns ranks to students based on their GPA in descending order.
4.2 Ranking Functions: RANK(), DENSE_RANK(), ROW_NUMBER()
• RANK(): Assigns the same rank to rows with identical values, creating gaps in the ranking
sequence.
SELECT ID , GPA , RANK () OVER ( ORDER BY GPA DESC ) AS rank
FROM student_grades ;
• DENSE_RANK(): Similar to RANK(), but without gaps in the ranking sequence when there are
ties.
SELECT ID , GPA , DENSE_RANK () OVER ( ORDER BY GPA DESC ) AS dense_rank
FROM student_grades ;
• ROW_NUMBER(): Assigns a unique sequential number to each row, regardless of ties.
SELECT ID , GPA , ROW_NUMBER () OVER ( ORDER BY GPA DESC ) AS row_num
FROM student_grades ;
ROW_NUMBER GPA RANK DENSE_RANK
1 4.0 1 1
2 4.0 1 1
3 3.8 3 2
4.3 Partitioned Ranking
Ranks students within each department based on their GPA using window functions, which is more
efficient than subqueries.
SELECT ID , dept_name , RANK () OVER ( PARTITION BY dept_name ORDER BY GPA
DESC ) AS dept_rank
FROM dept_grades
ORDER BY dept_name , dept_rank ;
ID dept_name GPA ID dept_name dept_rank
1 Math 3.8 1 Math 1
2 Science 3.5 3 Math 1
3 Math 3.8 2 Science 1
4 Science 3.2 4 Science 2
Table 3: Sample Input Table 4: Sample Output
11
Winter 2024 Triggers, Cursors, Recursive Queries and Advanced Aggregation Features Lab 2
5 Windowing Functions
Windowing functions perform calculations across a set of table rows that are related to the current
row. They are useful for tasks like calculating moving averages, running totals, and more.
5.1 3-Day Moving Average
Calculates a 3-day moving average of sales by summing the current day’s value with the values of
the preceding and following days.
SELECT date , SUM ( value ) OVER ( ORDER BY date ROWS BETWEEN 1 PRECEDING AND
1 FOLLOWING ) AS moving_avg
FROM sales ;
date value
2024-09-17 100 date moving_avg
2024-09-18 150 2024-09-17 250
2024-09-19 200 2024-09-18 450
2024-09-20 250 2024-09-19 600
2024-09-21 300 2024-09-20 750
2024-09-21 550
Table 6: Sample Output
Table 5: Sample Input
5.2 Running Total by Account
Calculates a running total of transaction values for each account.
SELECT account_number , date_time ,
SUM ( value ) OVER (
PARTITION BY account_number
ORDER BY date_time
ROWS UNBOUNDED PRECEDING
) AS running_total
FROM transaction
ORDER BY account_number , date_time ;
ac_no date_time total acc_no date_time run_total
A001 2024-09-17 10:00:00 100 A001 2024-09-17 10:00:00 100
A001 2024-09-18 12:00:00 150 A001 2024-09-18 12:00:00 250
A002 2024-09-17 09:30:00 200 A001 2024-09-19 14:00:00 500
A001 2024-09-19 14:00:00 250 A002 2024-09-17 09:30:00 200
A002 2024-09-18 11:00:00 300 A002 2024-09-18 11:00:00 500
Table 7: Sample Input Table 8: Sample Output
12
Winter 2024 Advanced Data Manipulation Lab 3
1 Join
Joins are used to combine rows from two or more tables based on related columns.
INNER JOIN
Figure 1: Sample of How Inner Join works
Another example:
SELECT S . STUDENT_NAME , C . COURSE_NAME
FROM STUDENT S
INNER JOIN ENROLLMENT E ON S . STUDENT_ID = E . STUDENT_ID
INNER JOIN COURSE C ON E . COURSE_ID = C . COURSE_ID ;
This query aims to retrieve the names of students along with the courses they are enrolled
in by joining three tables: STUDENT, ENROLLMENT, and COURSE.
S_ID C_ID
S_ID S_NAME DEPT C_ID C_NAME DEPT
1 101
1 Alice CSE 2 102 101 Database Systems CSE
2 Bob EEE 1 103 102 Circuit Analysis EEE
3 Carol MCE 3 101 103 Operating Systems CSE
Table 1: Student Table Table 2: ENROLLMENT Ta- Table 3: COURSE Table
ble
Table 4: Before Tables
2
Winter 2024 Advanced Data Manipulation Lab 3
S_ID S_NAME DEPT C_ID
1 Alice CSE 101
1 Alice CSE 103
2 Bob EEE 102
3 Carol MCE 101
Table 5: Intermediate Result (Table A)
S_ID S_NAME DEPT C_ID C_NAME
1 Alice CSE 101 Database Systems
1 Alice CSE 103 Operating Systems
2 Bob EEE 102 Circuit Analysis
3 Carol MCE 101 Database Systems
Table 6: Intermediate Result (Table B)
S_NAME C_NAME
Alice Database Systems
Alice Operating Systems
Bob Circuit Analysis
Carol Database Systems
Table 7: Final Result: After INNER JOIN with COURSE
NATURAL JOIN
Natural join is an SQL join operation that creates a join on the base of the common columns
in the tables. To perform natural join there must be one common attribute(Column) be-
tween two tables. Natural join will retrieve from multiple relations. It works in three steps.
SELECT *
FROM TABLE1
NATURAL JOIN TABLE2 ;
• A natural join matches columns with the same names in both tables and only includes
rows where the values in those columns are equal (consistent tuples).
• It excludes rows where the values don’t match (inconsistent tuples).
• After joining, it removes one of the duplicate columns from the result set.
SELECT *
FROM STUDENT
NATURAL JOIN ENROLLMENT ;
3
Winter 2024 Advanced Data Manipulation Lab 3
S_ID C_ID
S_ID S_NAME DEPT C_ID C_NAME DEPT
1 101
1 Alice CSE 2 102 101 Database Systems CSE
2 Bob EEE 1 103 102 Circuit Analysis EEE
3 Carol MCE 3 101 103 Operating Systems CSE
Table 8: Student Table Table 9: ENROLLMENT Ta- Table 10: COURSE Table
ble
Table 11: Before Tables
S_ID S_NAME DEPT C_ID
1 Alice CSE 101
1 Alice CSE 103
2 Bob EEE 102
3 Carol MCE 101
Table 12: After Natural Join
2 String SQL Functions
1. CONCAT: Used to concatenate two or more strings.
SELECT CONCAT(’Hello, ’, ’World!’) AS Greeting
FROM dual;
2. INITCAP: Capitalizes the first letter of each word in a string.
SELECT INITCAP(’hello world’) AS Proper_Case
FROM dual;
3. LENGTH: Returns the length of a string.
SELECT LENGTH(’Hello, World!’) AS String_Length
FROM dual;
4. UPPER and LOWER: Converts text to uppercase or lowercase.
SELECT UPPER(’hello world’) AS Uppercase_Text
FROM dual;
SELECT LOWER(’HELLO WORLD’) AS Lowercase_Text
FROM dual;
4
Winter 2024 Advanced Data Manipulation Lab 3
5. SUBSTR: Extracts a portion of a string.
SELECT SUBSTR(’Hello, World!’, 1, 5) AS Substring
FROM dual;
6. INSTR: Returns the position of a substring in a string.
SELECT INSTR(’Hello, World!’, ’World’) AS Position
FROM dual;
7. LPAD and RPAD: Pads a string to the left or right with a specified character.
SELECT LPAD(’Hello’, 10, ’*’) AS Left_Padded
FROM dual;
SELECT RPAD(’Hello’, 10, ’*’) AS Right_Padded
FROM dual;
8. LTRIM and RTRIM: Removes leading or trailing spaces from a string.
SELECT LTRIM(’ Hello’) AS Trimmed_Left
FROM dual;
SELECT RTRIM(’Hello ’) AS Trimmed_Right
FROM dual;
9. COUNT: Counts the number of rows returned.
SELECT COUNT(*) AS Total_Rows
FROM dual;
5
Winter 2024 Aggregation, Grouping, ROLLUP, and CUBE in SQL Lab 4
1 Topics
1.1 Semi-Structured Data
Regular expressions allow us to search for complex patterns in string data. Common use
cases include extracting or filtering data based on specific patterns.
Example: Extracting a 4-digit year from string data using REGEXP_SUBSTR.
-- Create and insert data into table
DROP TABLE t1 ;
CREATE TABLE t1 ( data VARCHAR2 (50) ) ;
INSERT INTO t1 VALUES ( ’ FALL 2014 ’) ;
INSERT INTO t1 VALUES ( ’2014 CODE -B ’) ;
INSERT INTO t1 VALUES ( ’ CODE - A 2014 CODE -D ’) ;
INSERT INTO t1 VALUES ( ’ ADSHLHSALK ’) ;
INSERT INTO t1 VALUES ( ’ FALL 2004 ’) ;
COMMIT ;
-- Select rows containing the year >= 2014
SELECT *
FROM t1
WHERE TO_NUMBER ( REGEXP_SUBSTR ( data , ’\ d {4} ’) ) >= 2014;
Input:
FALL 2014
2014 CODE-B
CODE-A 2014 CODE-D
ADSHLHSALK
FALL 2004
Output:
FALL 2014
2014 CODE-B
CODE-A 2014 CODE-D
1.2 GROUP BY and HAVING
The GROUP BY clause is used for aggregating data, while the HAVING clause filters groups
based on aggregate conditions.
Example: Summing sales by department and filtering groups with sales over 1000.
-- Drop table if it exists
DROP TABLE order_details ;
-- Create table order_details
CREATE TABLE order_details (
department VARCHAR2 (50) ,
sales NUMBER
);
-- Insert data into order_details
INSERT INTO order_details ( department , sales ) VALUES ( ’ Electronics
’ , 1200) ;
INSERT INTO order_details ( department , sales ) VALUES ( ’ Furniture ’ ,
900) ;
2
Winter 2024 Aggregation, Grouping, ROLLUP, and CUBE in SQL Lab 4
INSERT INTO order_details ( department , sales ) VALUES ( ’ Electronics
’ , 1500) ;
INSERT INTO order_details ( department , sales ) VALUES ( ’ Furniture ’ ,
2000) ;
INSERT INTO order_details ( department , sales ) VALUES ( ’ Clothing ’ ,
1100) ;
-- Commit the transaction
COMMIT ;
-- Group sales by department and filter by total sales > 1000
SELECT department , SUM ( sales ) AS total_sales
FROM order_details
GROUP BY department
HAVING SUM ( sales ) > 1500;
Input:
Department Sales
Electronics 1200
Furniture 900
Electronics 1500
Furniture 2000
Clothing 1100
Output:
Department Total Sales
Electronics 2700
Furniture 2900
1.3 ROLLUP for Subtotals
The ROLLUP operator generates subtotals for specified columns in a GROUP BY query.
Example: Calculating subtotals for sales by department.
-- Generate subtotals using ROLLUP
SELECT department , SUM ( sales ) AS total_sales
FROM order_details
GROUP BY ROLLUP ( department )
ORDER BY department ;
Output:
Department Total Sales
Clothing 1100
Electronics 2700
Furniture 2900
6700 -- Grand Total
1.4 CUBE for Cross-Tabulation
The CUBE operator calculates subtotals for all possible combinations of grouping columns.
Example: Counting employees by department and designation using CUBE.
3
Winter 2024 Aggregation, Grouping, ROLLUP, and CUBE in SQL Lab 4
-- Drop table if it exists
DROP TABLE employees ;
-- Create table employees with department names
CREATE TABLE employees (
employee_id NUMBER ,
department VARCHAR2 (50) ,
designation VARCHAR2 (50) ,
hire_date DATE
);
-- Insert data into employees
INSERT INTO employees ( employee_id , department , designation ,
hire_date )
VALUES (1 , ’ Sales ’ , ’ Manager ’ , TO_DATE ( ’15 -02 -2005 ’ , ’DD - MM - YYYY ’) )
;
INSERT INTO employees ( employee_id , department , designation ,
hire_date )
VALUES (2 , ’ Sales ’ , ’ Manager ’ , TO_DATE ( ’21 -01 -2007 ’ , ’DD - MM - YYYY ’) )
;
INSERT INTO employees ( employee_id , department , designation ,
hire_date )
VALUES (3 , ’ Human Resources ’ , ’ Assistant ’ , TO_DATE ( ’10 -05 -2010 ’ , ’
DD - MM - YYYY ’) ) ;
-- Commit the transaction
COMMIT ;
-- Count employees with subtotals for all combinations
SELECT department , designation , COUNT (*) AS total
FROM employees
GROUP BY CUBE ( department , designation )
ORDER BY department , designation ;
Output:
Department Designation Total
Human Resources Assistant 1
Human Resources 1
Sales Manager 2
Sales 2
3 -- Grand Total
1.5 ROLLUP with GROUPING Function
ROLLUP: Generates subtotals and grand totals for hierarchical groupings, aggregating data
at multiple levels.
GROUPING(): A function that returns 1 for columns that are aggregated and 0 for columns
that have regular values. This helps identify whether a NULL in the result set is due to
aggregation or represents missing data.
SELECT
category ,
EXTRACT ( YEAR FROM sale_date ) AS year ,
SUM ( total_price ) AS total_revenue ,
4
Winter 2024 Aggregation, Grouping, ROLLUP, and CUBE in SQL Lab 4
GROUPING ( category ) AS category_group ,
GROUPING ( year ) AS year_group
FROM
sales
JOIN
books ON sales . book_id = books . book_id
JOIN
categories ON books . category_id = categories . category_id
GROUP BY
ROLLUP ( category , year ) ;
Category Year Total Revenue Category Group Year Group
Fiction 2022 5000 0 0
Fiction NULL 5000 0 1
NULL NULL 8000 1 1
Table 1: ROLLUP with GROUPING Function Example Output