0% found this document useful (0 votes)
293 views56 pages

Data Warehouse Management Systems

The document describes the design of a data warehouse for a scale model supplier. It includes: 1. The logical design with the original database schema and star schema with dimensions and fact tables. 2. The physical design with CREATE TABLE statements to define the dimension tables for customers, employees, offices, products, and date. It also includes the data dictionary. 3. The ETL process with scripts for initial and subsequent loading of the dimensions and fact tables. 4. Sample business analytics reports generated from the data warehouse including performance reports by employee, growth reports by year and region, and analysis reports by product or customer attributes.

Uploaded by

keohjx-pm19
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
293 views56 pages

Data Warehouse Management Systems

The document describes the design of a data warehouse for a scale model supplier. It includes: 1. The logical design with the original database schema and star schema with dimensions and fact tables. 2. The physical design with CREATE TABLE statements to define the dimension tables for customers, employees, offices, products, and date. It also includes the data dictionary. 3. The ETL process with scripts for initial and subsequent loading of the dimensions and fact tables. 4. Sample business analytics reports generated from the data warehouse including performance reports by employee, growth reports by year and region, and analysis reports by product or customer attributes.

Uploaded by

keohjx-pm19
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 56

Bachelor of Computer Science (Honours) in Data Science

BAIT 3003 Data Warehouse (202201)


Student Name Student ID Signature

Yeap Ying Thung 21PMR07272 Thung

Khoo Shi Zhao 21PMR07261 Zhao

Ng Theng Yang 21PMR07265 Yang

Keoh Ji Xiang 21PMR07260 Xiang


BAIT3003 Data Warehouse Technology
Assignment Assessment Form
Ta Task Weighta Criteria Ratings Mar CLO Yea Kho Ng Keo
sk Descriptio ge ks p o Shi Then h Ji
No ns Ying Zha g Xian
. Thu o Yang g
ng

1 Design of 5% 1
Include the relevant dimensions.
Data
warehous
Excellent (5) Good (4) Moderate (2-3) Poor (0-1)
e Include the correct measures in the fact table.
(logical
design)

Design of 15% 1
Create TABLE statements
Data
warehous
e Appropriate data types and size ofExcellent
attributes(13-15) Good (10-12) Moderate (6-9) Poor (0-5)
(physical
design) Proper Integrity constraints

2 ETL 20% 1
(initial
loading)
Excellent (18-20) Good (14-17) Moderate (9-13) Poor (0-8)
VIEWS, SELECT,INSERT,PROCEDURES for each of the dimensions and fact table. Variety of techn
ETL 20% 1
(subsequ
ent
loading) VIEWS, SELECT,INSERT,PROCEDURES for each of the dimensions and fact table. Logic to scrub dirty data
Excellent (18-20) Good (15-17) Moderate (9-14) Poor (0-8)

3 *Business 30% 3
Analytic Clear and proper identification of information needs
queries
design Excellent (25-30) Good (16-24) Moderate (9-15) Poor (0-8)
(Individual
marks Flexible query to cater for variety of inputs, use of multiple tables
awarded))
Meaningful report handlings

Data values formatted accordingly

4 Assignmen 10% 1
Comprehensive coverage
t
Report
Quality of report presented Excellent (9-10) Good (7-8) Moderate (4-6) Poor (0-3)

All tasks numbered, header / footer used, proper formatting


Group Member: Task 3 marks Total marks
1. Yeap Ying Thung ( ) ( )
2. Khoo Shi Zhao ( ) ( )
3. Ng Theng Yang ( ) ( )
4. Keoh Ji Xiang ( ) ( )
Table of Contents
Introduction 1

Chapter 1 Design of Data Warehouse 2


1.1 Logical Design 2
1.1.1 Original Database 2
1.1.2 Star Schema Dimension and Fact Tables 3
1.2 Physical Design 4
1.2.1 Dimension Tables 4
1.2.1.1 Customer Dimension 4
1.2.1.2 Employees Dimension 4
1.2.1.3 Offices Dimension 4
1.2.1.4 Products Dimension 4
1.2.1.5 Date Dimension 5
1.2.3 Data Dictionary 6
1.2.3.1 Customer Dimension 6
1.2.3.2 Employees Dimension 6
1.2.3.3 Offices Dimension 7
1.2.3.4 Products Dimension 7
1.2.3.5 Date Dimension 8
1.2.3.6 Sales Fact 9

Chapter 2 Extract, Transform, Load Process 10


2.1 Script for initial loading 10
2.1.1 Customer Dimension 10
2.1.2 Employees Dimension 11
2.1.3 Offices Dimension 11
2.1.4 Products Dimension 12
2.1.5 Date Dimension 12
2.1.6 Sales Fact Dimension 14
2.2 Script for subsequent loading 15
2.2.1 Employees Dimension 15
2.2.2 Date Dimension 15
2.2.3 Customer Dimension 16

Chapter 3 Business Analytics Reports 16


3.1 Yeap Ying Thung 16
3.1.1 Employee Performance Report (Internal Report) 16
3.1.2 Company Growth Report 18
3.1.3 Ireland Top 10 Product Analysis Report 20
3.2 Khoo Shi Zhao 23
3.2.1 Company Overall Performance Report (Yearly) 23
3.2.2 Overall Office Performance Report 25
3.2.3 Report Based on Product Category 28
3.3 Ng Theng Yang 30
3.3.1 Office Branch Performance Report 30
3.3.2 Customer Sales Details Report 32
3.3.3 Overall Vendor Analytic Report 35
3.4 Keoh Ji Xiang 38
3.4.1 Annual Report for 2021 38
3.4.2 Monthly Performance Report 42
3.4.3 Customer Country Analysis 45
Introduction
Popular Scale Model Supplier (PSMS) is a well-known vehicle model supplier started in 2005. The
company has made a name for themselves by producing high-quality and detailed vehicle models at a
reasonable price. PSMS has customers from all around the world ranging from France to Ireland to the
United States of America. PSMS produces plastic models from popular manufacturers such as Revell,
Academy, Hasegawa, Tamiya, Hobbyboss, Trumpeter and other scale model vendors. PSMS’s product
categories include classic cars, motorcycles, planes, ships, trains, trucks, buses, and vintage cars. PSMS
produces upwards of 3500 models and outsources to over 60 producers to manufacture their model
vehicles and their models scale from 1 to 35 scale models to 1 to 700 scale models.

1
Chapter 1 Design of Data Warehouse

1.1 Logical Design

1.1.1 Original Database

2
1.1.2 Star Schema Dimension and Fact Tables

3
1.2 Physical Design

1.2.1 Dimension Tables

1.2.1.1 Customer Dimension


create table DIM_customers
(customer_key number not null,
customerNumber number(11) not null,
customerName varchar(50),
city varchar(50),
state varchar(50),
country varchar(50),
staffNo number(11),
creditLimit number(9,2),
primary key(customer_key)
);

1.2.1.2 Employees Dimension


create table DIM_employees
(employee_key number(5) not null,
employeeNumber number(11) not null,
lastName varchar(50),
officeCode varchar(10),
jobtitle varchar(50),
primary key(employee_key)
);

1.2.1.3 Offices Dimension


create table DIM_offices
(office_key number(5) not null,
officeCode varchar(10) not null,
city varchar(50),
state varchar(50),
country varchar(50),
postalcode varchar(15),
territory varchar(10),
primary key(office_key)
);

1.2.1.4 Products Dimension


create table DIM_products

4
(product_key number not null,
productCode varchar(15) not null,
productName varchar(70),
productLine varchar(50),
productScale varchar(10),
productVendor varchar(50),
buyPrice number(7,2),
MSRP number(7,2),
primary key(product_key)
);

1.2.1.5 Date Dimension


create table DIM_Date
(date_key number not null,
Calendar_date date,
Day_of_week number(1),
Day_Num_Cal_Month number(2),
Day_Num_Cal_Year number(3),
Last_Day_in_Month_Ind char(1),
Cal_Week_End_Date date,
Cal_Week_in_Year number(2),
Cal_Month_Name varchar(9),
Cal_Month_No_in_Year number(2),
Cal_Year_Month char(7),
Cal_Quarter char(2),
Cal_Year_Quarter char(6),
Cal_Year number(4),
Holiday_Ind char(1),
Weekday_Ind char(1),
primary key(date_key)
);

1.2.2 Fact Table


create table SALES_FACT
(date_key number not null,
product_key number not null,
customer_key number not null,
office_key number(5) not null,
employee_key number(5) not null,
quantityOrdered number(4),
priceEach number(7,2),
lineTotal number(9,2),
orderNumber number(11),
orderLineNumber number(3),

5
primary key(date_key, product_key, customer_key,
office_key, employee_key,orderNumber)
);

1.2.3 Data Dictionary

1.2.3.1 Customer Dimension

Attribute Data Type Constraint Description Example

customer_key (PK) NUMBER NOT NULL Customer key 1003262,100326


link to sales 3
facts

customerNumber NUMBER(11) NOT NULL Customer’s ID 103081,103082

customerName VARCHAR(50) - Customer’s Lisboa


name Souvenic,
Online Diecast

city VARCHAR(50) - Customer’s city New York City,


Boston

state VARCHAR(50) - Customer’s state New York,


Massachusetts

country VARCHAR(50) - Customer’s United States of


country America

staffNo NUMBER(11) - The staff ID who 1504


handle this
customer

creditLimit NUMBER(9,2) - The maximum 114200, 70700


limit of the
credit allowed.

1.2.3.2 Employees Dimension

Attribute Data Type Constraint Description Example

employee_key NUMBER(5) NOT NULL Employee key 10012


(PK) link to sales
facts

6
employeeNumber NUMBER(11) NOT NULL Employee’s ID 1611, 1501

lastName VARCHAR(50) - Last Name of Khoo, Yeap,


employee Keoh

officeCode VARCHAR(10) - Employee’s 1 to 7


Office ID

jobtitle VARCHAR(50) - Employee’s Job Sales Rep.


title

1.2.3.3 Offices Dimension

Attribute Data Type Constraint Description Example

office_key (PK) NUMBER(5) NOT NULL Office key link 10001


to sales facts

officeCode VARCHAR(10) NOT NULL Office’s ID 1 to 7

city VARCHAR(50) - Office’s City New York City,


Boston

state VARCHAR(50) - Office’s State New York,


Massachusetts

country VARCHAR(50) - Office’s United States of


Country America

postalcode VARCHAR(15) - Office’s Postal 10511, 99950


Code

territory VARCHAR(10) - Office’s APAC


Territory

1.2.3.4 Products Dimension

Attribute Data Type Constraint Description Example

Product_key (PK) NUMBER NOT NULL Product key link 100017


to sales facts

productCode VARCHAR(15) NOT NULL Product’s ID S18_1001062,


S18_1001076

7
productName VARCHAR(70) - Product’s name Triumph
Spitfire, Buick
Runabout

productLine VARCHAR(50) - Product’s Motorcycles,


category Planes, cars

productScale VARCHAR(10) - Product’s scale 1:700, 1:18

productVendor VARCHAR(50) - Product’s Autoart Studio


supplier Design

buyPrice NUMBER(7,2) - Product’s price 91.9,43.2


bought from
supplier

MSRP NUMBER(7,2) - Product’s price 121.08, 50.31


that sells to the
customer

1.2.3.5 Date Dimension

Attribute Data Type Constraint Description Example

Date_key (PK) NUMBER NOT NULL Date key link to 100005


sales facts

Calendar_date DATE - Date of calendar 01/01/2019-


31/01/2022

Day_of_week NUMBER(1) - Day in a week 1 to 7

Day_Num_Cal_ NUMBER(2) - Day in a month 1 to 31


Month

Day_Num_Cal_ NUMBER(3) - Day in a year 1 to 366


Year

Last_Day_in_M CHAR(1) - Is it the last Day Yes or No


onth_Ind of the month?

Cal_Week_End_ DATE - Is it the last day Yes or No


Date of the week?

Cal_Week_in_Y NUMBER(2) - Week in a year 1 to 52


ear

8
Cal_Month_Na VARCHAR(9) - Month’s name Jan, Feb
me

Cal_Month_No_ NUMBER(2) - Month in a year 1 to 12


in_Year

Cal_Year_Mont CHAR(7) - Year and Month 2022-04


h

Cal_Quarter CHAR(2) - Quarter of the ‘Q1’ to ‘Q4’


year

Cal_Year_Quart CHAR(6) - Year and quarter 2022Q2


er

Cal_Year NUMBER(4) - Year 2022

Holiday_Ind CHAR(1) - Is it holiday? Yes or No

Weekday_Ind CHAR(1) - Is it weekday? Yes or No

1.2.3.6 Sales Fact

Attribute Data Type Constraint Description Example

date_key (PK) NUMBER NOT NULL Date key link to 100005


sales facts

product_key (PK) NUMBER NOT NULL Product key link 100017


to sales facts

customer_key (PK) NUMBER NOT NULL Customer key 1003262


link to sales
facts

office_key (PK) NUMBER(5) NOT NULL Office key link 10001


to sales facts

Employee_key NUMBER(5) NOT NULL Employee key 10012


(PK) link to sales
facts

quantityOrdered NUMBER(4) - Quantity 5


Ordered of the
transaction

9
priceEach NUMBER(7,2) - Price for each 102.05
product

lineTotal NUMBER(9,2) - Total Prices 510.25

orderNumber NUMBER(11) NOT NULL Order number of 101303


the transaction

orderLineNumber NUMBER(3) - Order Line 1,2,3,4


number of the
transaction

Chapter 2 Extract, Transform, Load Process

2.1 Script for initial loading

2.1.1 Customer Dimension


drop sequence customer_seq;
create sequence customer_seq
start with 1000001
increment by 1;

drop table DIM_customers;


CREATE TABLE DIM_customers
(customer_key number not null,
customerNumber number(11) not null,
customerName varchar(50),
city varchar(50),
state varchar(50),
country varchar(50),
staffNo number(11),
creditLimit number(9,2),
PRIMARY KEY(customer_key)
);

insert into DIM_customers


select customer_seq.nextval,
customerNumber, customerName, city, state, country,
SALESREPEMPLOYEENUMBER, creditLimit
from customers;

select customer_key,
customerNumber, customerName, creditLimit
from DIM_customers;

10
2.1.2 Employees Dimension
drop sequence emp_seq;
create sequence emp_seq
start with 10001
increment by 1;

drop table DIM_employees;


CREATE TABLE DIM_employees
(employee_key number(5) not null,
employeeNumber number(11) not null,
lastName varchar(50),
officeCode varchar(10),
jobtitle varchar(50),
PRIMARY KEY(employee_key)
);

insert into DIM_employees


select emp_seq.nextval,
employeeNumber, lastName, officeCode, jobtitle
from employees;
select employeeNumber, lastName, officeCode, jobtitle
from DIM_employees;

2.1.3 Offices Dimension


drop sequence office_seq;
create sequence office_seq
start with 10001
increment by 1;

drop table DIM_offices;


CREATE TABLE DIM_offices
(office_key number(5) not null, -- surrogate key
officeCode varchar(10) not null,
city varchar(50),
state varchar(50),
country varchar(50),
postalcode varchar(15),
territory varchar(10),
PRIMARY KEY(office_key)
);
-- ETL ...Extract
insert into DIM_offices
select office_seq.nextval, officeCode, city, state,
country, postalCode, territory
from offices;

-- coding, if state is null and DIM_xxx table specify NOT NULL


load data into cursor
while (cursor row exist)
if state is null, replace with CITY or COUNTRY data

11
insert into DIM_office ....
end if
end loop
select officeCode, state
from offices;

2.1.4 Products Dimension


drop sequence product_seq;
create sequence product_seq
start with 100001
increment by 1;

drop table DIM_products;


CREATE TABLE DIM_products
(product_key number not null,
productCode varchar(15) not null,
productName varchar(70),
productLine varchar(50),
productScale varchar(10),
productVendor varchar(50),
buyPrice number(7,2),
MSRP number(7,2),
PRIMARY KEY(product_key)
);

insert into DIM_products


select product_seq.nextval,
productCode, productName, productLine,
productScale, productVendor, buyPrice, MSRP
from products;

select productCode, productName, buyPrice, MSRP


from DIM_products;

2.1.5 Date Dimension


create sequence date_seq
start with 100001
increment by 1;

drop table DIM_Date;

CREATE TABLE DIM_Date


(date_key number not null,
Calendar_date date,
Day_of_week number(1),
Day_Num_Cal_Month number(2),
Day_Num_Cal_Year number(3),
Last_Day_in_Month_Ind char(1),
Cal_Week_End_Date date,

12
Cal_Week_in_Year number(2),
Cal_Month_Name varchar(9),
Cal_Month_No_in_Year number(2),
Cal_Year_Month char(7),
Cal_Quarter char(2),
Cal_Year_Quarter char(6),
Cal_Year number(4),
Holiday_Ind char(1),
Weekday_Ind char(1),
PRIMARY KEY(date_key)
);

declare
every_date date;
end_date date;
v_day_of_week number(1);
v_day_of_month number(2);
v_day_of_year number(3);
last_day_month_ind char(1);
v_week_end_date date;
v_week_in_year number(2);
v_month_name varchar(9);
v_month_no number(2);
v_year_month char(7);
v_quarter char(2);
v_year_quarter char(6);
v_year number(4);
v_holiday_ind char(1);
v_weekday_ind char(1);

begin
every_date := to_date('01/01/2019','dd/mm/yyyy');
end_date := to_date('31/01/2022','dd/mm/yyyy');
v_holiday_ind :='N';

while (every_date <= end_date) LOOP


v_day_of_week := to_char(every_date,'D');
v_day_of_month := to_char(every_date,'DD');
v_day_of_year := to_char(every_date,'DDD');
if every_date = Last_Day(every_date) then
last_day_month_ind := 'Y';
end if;

v_week_end_date := every_date+(7-
(to_char(every_date,'d')));

v_week_in_year := to_char(every_date,'IW');
v_month_name := to_char(every_date,'MONTH');
v_month_no := extract (month from every_date);
v_year_month := to_char(every_date,'YYYY-MM');

13
if (v_month_no<=3) then
v_quarter := 'Q1';
elsif (v_month_no<=6) then
v_quarter := 'Q2';
elsif (v_month_no<=9) then
v_quarter := 'Q3';
else
v_quarter := 'Q4';
end if;

v_year := extract (year from every_date);


v_year_quarter := v_year||v_quarter;

if (v_day_of_week between 2 and 6) then


v_weekday_ind := 'Y';
else
v_weekday_ind := 'N';
end if;

insert into DIM_date values(date_seq.nextval,


every_date,
v_day_of_week,
v_day_of_month,
v_day_of_year,
last_day_month_ind,
v_week_end_date,
v_week_in_year,
v_month_name,
v_month_no,
v_year_month,
v_quarter,
v_year_quarter,
v_year,
v_holiday_ind,
v_weekday_ind
);
every_date := every_date + 1;
end LOOP;
end;

2.1.6 Sales Fact Dimension


drop table sales_fact;

CREATE TABLE SALES_FACT


(date_key number not null,
product_key number not null,
customer_key number not null,
office_key number(5) not null,
employee_key number(5) not null,
quantityOrdered number(4),
priceEach number(7,2),

14
lineTotal number(9,2),
orderNumber number(11),
orderLineNumber number(3),
PRIMARY KEY(date_key, product_key, customer_key,
office_key, employee_key,orderNumber)
);

insert into Sales_fact


select date_key, product_key, customer_key,
office_key, employee_key,
B.quantityordered, B.priceEach,
(B.quantityordered*B.priceEach) LineTotal,
A.orderNumber, B.orderLineNumber
from Dim_date D
join orders A
on trunc(D.calendar_date) = trunc(A.orderDate)
join orderDetails B
on A.orderNumber = B.orderNumber
join Dim_products P
on P.productCode = B.productCode
join Dim_customers C
on A.customerNumber = C.customerNumber
join Dim_employees E
on E.employeeNumber = C.staffNo
join Dim_offices O
on E.officeCode = O.officeCode;

2.2 Script for subsequent loading

2.2.1 Employees Dimension


insert into DIM_EMPLOYEES values(
99999, 9999, 'UNDEFINED', 1, 'UNDEFINED');

2.2.2 Date Dimension


create or replace procedure Update_Hol_prc
(IN_DATE DATE ) is
begin
update DIM_DATE
set Holiday_Ind = 'Y'
where CALENDAR_DATE = in_date;

if SQL%FOUND then
dbms_output.put_line('Input date: '|| IN_DATE||
' updated as Holiday');
else
dbms_output.put_line('No such date');
end if;
end;

15
/

ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YYYY';

exec Update_Hol_prc('01-JAN-2022')

2.2.3 Customer Dimension


UPDATE DIM_CUSTOMERS
SET staffNo = 9999
where staffNo IS NULL;

Chapter 3 Business Analytics Reports

3.1 Yeap Ying Thung

3.1.1 Employee Performance Report (Internal Report)


The employee performance report generated by PSMS will contain their employee’s information such as
their employee number and employee name. And regarding their performance, the report will focus on the
number of orders the employee has completed and the total sales of the orders in RM.

This report allows the user to view the overall performance of the employees and know which employee
has better performance and which employee is lacking in performance in the company. It can also help
the company to figure out the mean sales figures of their employees.

For example, by referring to the report, we can see that the employee - Castillo with ‘1401’ employee
number has the greatest working performance as she has completed 119334 orders and gain around 65
million sales for the company

SQL Query
--Internal Report
--Employee Performance Report
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE PROCEDURE prc_employeeperformancereport IS


-- variables declaration
v_employeenumber dim_employees.employeenumber%TYPE;
v_employeename dim_employees.lastname%TYPE;
v_customercount number;
v_ordercount number;
v_sales number;

CURSOR csr_orderperemployee IS

16
SELECT e.employeenumber, e.lastname, count(s.ordernumber),
sum(s.linetotal) as sales
FROM dim_employees e, sales_fact s
where e.employee_key = s.employee_key
group by e.employeenumber, e.lastname
order by sum(s.linetotal) desc;

BEGIN
DBMS_OUTPUT.PUT_LINE(LPAD ('=',85,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',58,' ')||
LPAD(' | ',26));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Internal Report',46,' ')||LPAD('
| ',38));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Employee Performance Report',52,'
') || ' ' || LPAD(' | ',31));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',81) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,71, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',85,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Employee Number',15)||' | '||
RPAD('Employee Name',15)||' | '||LPAD('Order Count',16)||' | '||
LPAD('Total Sales (RM)',26)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',85,'-'));
OPEN csr_orderperemployee;

LOOP
FETCH csr_orderperemployee INTO v_employeenumber,
v_employeename, v_ordercount, v_sales;
EXIT WHEN csr_orderperemployee%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_employeenumber,15)|| ' | '
|| RPAD(v_employeename,15)||' | ' ||
LPAD(to_char(v_ordercount,'999,999,999'),16)||' | '||
LPAD(to_char(v_sales,'999,999,999.99'),26)||' |');
END LOOP;

CLOSE csr_orderperemployee;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',85,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',46,' ') || ' ' ||
LPAD(' | ',37));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',85,'-'));

END;
/

exec prc_employeeperformancereport;

17
Results

3.1.2 Company Growth Report


The company growth report will display the total number of orders, profit, and growth rate percentage
based on the year starting from 2016 to 2021.

The purpose of this report is to trace out the growth rate based on base year. It can see the growth rate
from 2016 to 2022. It can let the stakeholder to refer in order to make a better and clearer decision by
investing in the company.

For example, if we assume that the company starts up from 2016, then we will take it as the base year.
After 2016, in 2017, the company’s sales totaled 235k of orders and gained a profit of 59 million. Then,
the growth rate is calculated by using the profit divided by the base year(2016) each year and get the
company growth rate. So far as we can see, the sales of the company in 2016 is the best and it hasn’t been
surpassed yet even until 2021. 2022 is excluded from this report as it is an annual report and 2022 is not
over yet.

SQL Query
--Company Performance Growth Report

18
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE PROCEDURE prc_growthreport IS


v_year number(6);
v_countrecord number;
v_profit number(15,2);
v_baseyear number(15,2);

CURSOR csr_getalldata IS
select d.cal_year as year, count(s.ordernumber) as
countOfRecord, sum(s.linetotal) - sum(p.buyprice * s.quantityordered)
as profit,
--cal compare_base_year
coalesce(
(sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) /
(select (sum(s.linetotal) - sum(p.buyprice *
s.quantityordered))
from sales_fact s
join dim_products p on s.product_key = p.product_key
join dim_date d on s.date_key = d.date_key
where d.cal_year = '2016'),0) * 100 as
compare_base_year
from sales_fact s
join dim_date d on s.date_key = d.date_key
join dim_products p on s.product_key = p.product_key
where d.cal_year NOT LIKE '2022'
group by d.cal_year
order by d.cal_year;

BEGIN
OPEN csr_getalldata;

DBMS_OUTPUT.PUT_LINE(LPAD ('=',81,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',54,' ')||
LPAD(' | ',26));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Company Growth Report',48,' ') ||
' ' || LPAD(' | ',31));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',77) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,66, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',81,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Year',6)||' | '||LPAD('Number of
Orders',18)||' | '||LPAD('Profit (RM)',16)||' | '||
LPAD('Growth Rate to Base Year (%)',28)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',81,'-'));

19
LOOP
FETCH csr_getalldata INTO v_year, v_countrecord, v_profit,
v_baseyear;
EXIT WHEN csr_getalldata%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_year,6)|| ' | '||
LPAD(to_char(v_countrecord,'999,999,999'),18)|| ' | ' ||
LPAD(to_char(v_profit,'999,999,999.99'),16)
||' | '|| LPAD(to_char(v_baseyear,'999,999,990.99'),28)||'
|');
END LOOP;
CLOSE csr_getalldata;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',81,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',46,' ') || ' ' ||
LPAD(' | ',33));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',81,'-'));

END;
/

exec prc_growthreport;

Results

3.1.3 Ireland Top 10 Product Analysis Report


In this report it will show the top 10 most popular products sold in Ireland, which is the top selling
country for PSMS in 2021. It will display the product name along with the number of sales of the top 10
products. It will also show the percentage of contribution the product contributed to the total number of
sales.This report will show in detail the top 10 products being sold in the company’s top sales country
which is Ireland. This report allows the company to know which of their products are more popular in one
of their highest sales countries.

20
For example, after the sales report based on the country is printed, we can see that the top sales country is
Ireland. After that, if we want to know the TOP 10 products that get the highest sales in Ireland, the sales
manager and marketing manager can refer to this report. From this report, they can see that the most
popular product is the Alpine Renault 1300 which has the highest sales, which is around 266K and this
sales contribute 2.03% to the total sales of Ireland.

SQL Query
--Top Sales Country - Ireland TOP 10 Product Analysis Report
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE VIEW productIrelandView AS


select concat(p.productcode,p.productname) as product,
sum(s.linetotal) as sales,
(ROUND((sum(s.linetotal)/(SELECT SUM(s.linetotal) from sales_fact
s join dim_customers c on c.customer_key = s.customer_key where
country = 'Ireland'))*100,2)) as contribution
from sales_fact s
join dim_products p on p.product_key = s.product_key
join dim_customers c on c.customer_key = s.customer_key
where country = 'Ireland'
group by concat(p.productcode,p.productname)
order by sum(s.linetotal) desc;

CREATE OR REPLACE PROCEDURE prc_irelandanalysis IS


-- variables declaration
v_product dim_products.productname%TYPE;
v_sales sales_fact.linetotal%TYPE;
v_contribution number;

CURSOR csr_topten IS
SELECT product, sales, contribution
FROM productIrelandView
where rownum <= 10;

BEGIN
DBMS_OUTPUT.PUT_LINE(LPAD ('=',91,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',63,' ')||
LPAD(' | ',27));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Ireland TOP 10 Product Analysis
Report',64,' ') || ' ' || LPAD(' | ',25));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',87) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,77, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',91,'-'));

--for header

21
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Product',50)||' | '||LPAD('Sales
(RM)',15)||' | '||LPAD('Contribution (%)',16)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',91,'-'));

OPEN csr_topten;

LOOP
FETCH csr_topten INTO v_product, v_sales, v_contribution;
EXIT WHEN csr_topten%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_product,50)|| ' | ' ||
LPAD(to_char(v_sales,'999,999,999.99'),15)||' | '||
LPAD(v_contribution,16)||' |');
END LOOP;

CLOSE csr_topten;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',91,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',46,' ') || ' ' ||
LPAD(' | ',43));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',91,'-'));

END;
/

exec prc_irelandanalysis;

Results

22
3.2 Khoo Shi Zhao

3.2.1 Company Overall Performance Report (Yearly)


In the company's overall performance report it will display the profit of the specific year and compare it
to the previous year so that the company is able to know if there was an improvement in the sales or did
the sales decrease instead. It will also calculate and display the profit ratio comparing the current year
profit with the previous year profit.

This report is able to let the company visualize their improvements over the years so that they can
determine which business tactic is the best for maximum improvements year after year.

For example, by referring to the report, the readers can know that the sales of the company grow during
2017 compared to 2016. The profit ratio will help the reader to have a clearer understanding about the
grow of the company sales as it also represents the company sales trend. If the profit ratio is over 1.00,
that means that the profit is growing by 3%+, else, it will represents that the profit drops.

SQL Query
--Company Overall Performance Report with Trend
set serveroutput on;
set linesize 200;
set pagesize 50;
drop procedure prc_comperformance;

CREATE OR REPLACE PROCEDURE prc_comperformance IS


v_year dim_date.cal_year%TYPE;
v_profit number(20,2);
v_profitpreyear number(20,2);
v_profitratio number(8,2);

CURSOR csr_getall IS
select d.cal_year as year, sum(s.linetotal) - sum(p.buyprice *
s.quantityordered) as profit,
coalesce(LAG(sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) OVER ( ORDER BY d.cal_year ),0) AS
Profit_Previous_Year,--Lag is use to delay one row
--cal profit ratio based on previous year formula(profit
this year / profit previous year)
coalesce(round((sum(s.linetotal) - sum(p.buyprice *
s.quantityordered))/
(LAG(sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) OVER ( ORDER BY d.cal_year )),2) ,0) AS
profit_ratio
from sales_fact s
join dim_date d on s.date_key = d.date_key

23
join dim_products p on s.product_key = p.product_key
where d.cal_year NOT LIKE '2022'
group by d.cal_year
order by d.cal_year;

BEGIN
OPEN csr_getall;

DBMS_OUTPUT.PUT_LINE(LPAD ('=',78,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',52,' ')||
LPAD(' | ',25));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Company Overall Performance
Report (Yearly)',56,' ') || ' ' || LPAD(' | ',20));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',74) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,63, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',78,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Year',6)||' | '||LPAD('Profit
(RM)',15)||' | '||
LPAD('Profit of Previous Year (RM)',30)||' | '||LPAD('Profit
Ratio',14)||' | ');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',78,'-'));

LOOP
FETCH csr_getall INTO v_year, v_profit, v_profitpreyear,
v_profitratio;
EXIT WHEN csr_getall%NOTFOUND;

if v_year IS NULL then


v_year := 0;

elsif v_profit IS NULL then


v_profit := 0;

elsif v_profitpreyear IS NULL then


v_profitpreyear := 0;

elsif v_profitratio IS NULL then


v_profitratio := 0;
end if;

DBMS_OUTPUT.put_line('| '||RPAD(v_year,6)|| ' | '||

24
LPAD(to_char(v_profit,'999,999,999.99'),15)
||' | ' ||
LPAD(to_char(v_profitpreyear,'999,999,990.99'),30)||' | '||
LPAD(to_char(v_profitratio,'990.99'),14)||
' | ');
END LOOP;
CLOSE csr_getall;
DBMS_OUTPUT.PUT_LINE(LPAD ('-',78,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',46,' ') || ' ' ||
LPAD(' | ',30));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',78,'-'));
END;
/

exec prc_comperformance;

Results

3.2.2 Overall Office Performance Report


The overall office report generated by PSMS contains information about their offices around the globe
and their respective office details such as the office code, which country the office is located in, the total
number of sales they have conducted, the total profit they generated and the percentage of contribution
they have contributed to the total revenue.

The purpose of this report is to compare the sales among the offices. It can create a competitive challenge
among the offices. So that, the employee in the office can contribute more effort in finding the sales to
increase the rank of the office.

For example, the office with the code 1 originates from USA has a total sales figure of 222 million+ and

25
a profit of 102 million+. The contribution beside indicates that the office contributed 27.88% to the
company’s overall revenue and is the best performing office branch of PSMS.

SQL Query
--Report Based on office (overall)
--office report

set serveroutput on;


set linesize 200;
set pagesize 50;

CREATE OR REPLACE PROCEDURE prc_officereport IS


v_officecode dim_offices.officecode%TYPE;
v_country dim_offices.country%TYPE;
v_sales number(13,2);
v_profit number(13,2);
v_contribution number(13,2);

CURSOR csr_getofficereport IS
select officecode, country, sum(s.linetotal) as sales,
sum(s.linetotal) - sum(s.quantityordered * p.buyprice) as
profit,
(round((sum(s.quantityordered * p.buyprice)/(select
sum(s.quantityordered * p.buyprice) from sales_fact s, dim_products p
where s.product_key = p.product_key))*100,2)) as contribution
from dim_offices o
join sales_fact s on s.office_key = o.office_key
join dim_products p on s.product_key = p.product_key
group by officecode, country
order by sum(s.linetotal) - sum(s.quantityordered *
p.buyprice) desc;

BEGIN
OPEN csr_getofficereport;

DBMS_OUTPUT.PUT_LINE(LPAD('=',92,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',63,' ')||LPAD(' | ',28));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Overall Office Performance
Report',62,' ') || ' ' || LPAD(' | ',28));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',88) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,76, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',92,'-'));

26
--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Office Code',13)||' | '||
RPAD('Country',11)||' | '||LPAD('Sales (RM)',18)||' | '||
LPAD('Profit (RM)',18)||' | '||LPAD('Contribution (%)',16)||' |
');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',92,'-'));

LOOP
FETCH csr_getofficereport INTO v_officecode, v_country,
v_sales, v_profit, v_contribution;
EXIT WHEN csr_getofficereport%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_officecode,13)|| ' | '||
RPAD(v_country,11)||' | '||
LPAD(to_char(v_sales,'999,999,999.99'),18)||
' | '|| LPAD(to_char(v_profit,'999,999,999.99'),18)||' | '||
LPAD(to_char(v_contribution,'999,999,999.99'),16)||' | ');
END LOOP;
CLOSE csr_getofficereport;
DBMS_OUTPUT.PUT_LINE(LPAD ('-',92,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',55,' ')||LPAD(' |
',36));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',92,'-'));

END;
/

exec prc_officereport;

Results

27
3.2.3 Report Based on Product Category
The report generated based on the product category will consist of information about the product line, top
3 highest sales product and the total number of sales in said product line. It will also show the ranking of
the products so that we will know the top 3 products in their respective product lines.

The purpose of this report is to find out the top 3 rank of each category of the product which has the
highest sales. This is to trace out our favorite product that most of the customers prefer. It can do the
forecasting and make promotions to increase the sales based on this report.

For example, the top 3 products from the “Motorcycles' ' product line are the Harley Davidson Eagle,
ranked 1, Suzuki XREO, ranked 2, and the Moto Guzzi 1100i, ranked 3. The rankings are ranked based
on the sales of the product type. By referring to this report, we can know the popular product among
customers and the revenue from each type of product.

SQL Query
--Report Based on Product Category (overall)
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE VIEW categoryProfitView AS


select productcode, productname, SUM(s.linetotal)-
SUM(p.buyPrice*s.quantityOrdered) as profitofEachProduct
from dim_products p
join sales_fact s on p.product_key = s.product_key
group by productcode, productname;

CREATE OR REPLACE VIEW typeSalesView AS


select p.productline, sum(s.linetotal) as sales,
concat(p.productcode,p.productname) as product,
RANK() over(PARTITION BY p.productline order by sum(s.linetotal)
desc) as productrank
from sales_fact s
join dim_products p on p.product_key = s.product_key
join categoryProfitView v on v.productcode = p.productcode
group by p.productline, concat(p.productcode,p.productname);

CREATE OR REPLACE PROCEDURE prc_productlinesales IS


-- variables declaration
v_productline dim_products.productline%TYPE;
v_product varchar2(100);
v_sales number(13,2);
v_custrank number(3);

28
CURSOR csr_getproductline IS
SELECT productline, product, sales, productrank from
typeSalesView
where productrank <= 3;

BEGIN
OPEN csr_getproductline;

DBMS_OUTPUT.PUT_LINE(LPAD('=',106,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',70,' ')||LPAD(' | ',35));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Report Based on Product
Category',69,' ') || ' ' || LPAD(' | ',35));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',102) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,90, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',106,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Product Line',17)||' | '||
RPAD('TOP 3 Highest Sales Product',40)||' | '||LPAD('Sales
(RM)',21)||' | '||LPAD('Rank',15)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',106,'-'));

LOOP
FETCH csr_getproductline INTO v_productline, v_product,
v_sales, v_custrank;
EXIT WHEN csr_getproductline%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_productline,17)|| ' | '||
RPAD(v_product,40)||' | '||
LPAD(to_char(v_sales,'999,999,999.99'),21)||
' | '||LPAD(v_custrank,15)||' | ');
END LOOP;
CLOSE csr_getproductline;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',106,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',60,' ') || ' ' ||
LPAD(' | ',44));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',106,'-'));

END;
/

exec prc_productlinesales;

29
Results

3.3 Ng Theng Yang

3.3.1 Office Branch Performance Report


In this report it will list the office code which directly correlates to the country the office is situated at. It
will also list which country the office code stands for. It will also show the number of customers each
office has served and the total number of orders each office has processed. Finally, it will divide the total
number of orders and the total number of customers from each office to find out the average orders per
customer.

The purpose of this report is to let the offices see the total number of customers and the total quantity of
the product that was bought by the customer. It can see the employees of the offices make some effort to
attract the customers and increase the sales.

For example, the office branch situated at USA with the office code 1, 3, and 2 has a total sale of
RM222,121,173.99, RM98,739,650.51, and RM79,354,171.06 and have total number of customers of
4,352, 1,920, and 1,536 respectively. The total number of orders are 162,195, 72,266, and 57,733. The
three branches in the USA each contribute 27.76%, 12.34% and 9.92% respectively to the total profit of
the company.

SQL Query

Results

30
--office code, country, number of customer, qty order, average of qty
if each customer buy
-- add sales
set serveroutput on;
set linesize 200;
set pagesize 50;

--Office Branch Performance Report


set serveroutput on;

CREATE OR REPLACE PROCEDURE prc_branchreport IS


v_officecode dim_offices.officecode%TYPE;
v_country dim_offices.country%TYPE;
v_sales number(15,2);
v_custcount number;
v_ordercount number;
v_contribution number(5,2);

CURSOR csr_getoffice IS
select o.officecode, o.country, sum(s.linetotal) as
total_sales, count(distinct s.customer_key) as number_Of_Customer,
count(distinct s.ordernumber) as number_Of_Order,
round(sum(s.linetotal)/(select sum(linetotal) from
sales_fact) * 100, 2) as Contribution
from dim_offices o
join sales_fact s on o.office_key = s.office_key
group by officecode, country
order by count(distinct s.customer_key) desc;

BEGIN
OPEN csr_getoffice;

DBMS_OUTPUT.PUT_LINE(LPAD ('=',108,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',72,' ')||
LPAD(' | ',35));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Office Branch Performance
Report',70,' ') || ' ' || LPAD(' | ',36));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',104) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,93, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',108,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Office Code',14)||' | '||

31
RPAD('Country',9)||' | '||LPAD('Sales (RM)',15)||' | '||LPAD('Total
Customer',16)||' | '||
LPAD('Number of Order',17)||' | '||LPAD('Contribution (%)',18)||'
|');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',108,'-'));

LOOP
FETCH csr_getoffice INTO v_officecode, v_country, v_sales,
v_custcount, v_ordercount, v_contribution;
EXIT WHEN csr_getoffice%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_officecode,14)|| ' | '||
RPAD(v_country,9)|| ' | ' ||
LPAD(to_char(v_sales,'999,999,999.99'),15)||' | '||
LPAD(to_char(v_custcount,'999,999,999'),16)||' | '
||LPAD(to_char(v_ordercount,'999,999,999'),17)||' | '||
LPAD(to_char(v_contribution,'999,999,990.99'),18)||' |');
END LOOP;
CLOSE csr_getoffice;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',108,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',60,' ') || ' ' ||
LPAD(' | ',46));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',108,'-'));
END;
/

exec prc_branchreport;

3.3.2 Customer Sales Details Report


The customer sales details report will generate the average of the sales per order that was made by the
customer. It will display the customer’s name, country, order count, total sales and the average of the
sales per order. It will list out the top 20 customers based on the average sales per order.

32
This report will find the potential customers and see the average of the sale of each of the customers
bought and the quantity of the customer ordered. It can let the user view the sales details of the potential
customers. Company can improve its sales analysis and response time to inquiries.

For example, the customer Oulu Toy Supplies from Finland placed an order of 70 units and resulted in a
total sale of RM50,635 and an average sale per order of RM723.35.

SQL Query
--customer average sales per order / customer report
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE VIEW productIrelandView AS


select customername, country, count(ordernumber) as ordercount,
sum(linetotal) as salescount , round((sum(linetotal) /
count(ordernumber)),2) as averageSalesPerOrder
from sales_fact s
join dim_customers c on s.customer_key = c.customer_key
group by customername, creditlimit, country
order by round((sum(linetotal) / count(ordernumber)),2) desc;

CREATE OR REPLACE PROCEDURE prc_customerreport IS


-- variables declaration
v_customername varchar2(100);
v_country dim_customers.country%TYPE;
v_ordercount number;
v_salestotal number(15,2);
v_averagesales number(15,2);

--get the top 20 customer list


CURSOR csr_top20custlist IS
SELECT *
FROM productIrelandView
where rownum <= 20;

BEGIN
DBMS_OUTPUT.PUT_LINE(LPAD ('=',109,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',73,' ')||
LPAD(' | ',35));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Customer Sales Details
Report',71,' ') || ' ' || LPAD(' | ',36));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',105) ||' | ');

33
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,95, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',109,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Customer Name',25)||' | '||
RPAD('Country',14)||' | '||LPAD('No. of Order',12)||' | '||LPAD('Sales
(RM)',13)||' |'||LPAD('Average Sales per Order (RM)',30)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',109,'-'));

OPEN csr_top20custlist;

LOOP
FETCH csr_top20custlist INTO v_customername, v_country,
v_ordercount, v_salestotal, v_averagesales;
EXIT WHEN csr_top20custlist%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_customername,26)||'| '||
RPAD(v_country,15)||'| '||RPAD(v_ordercount,13)||'| '||
RPAD(to_char(v_salestotal,'999,999,999'),13)|| ' | ' ||
LPAD(to_char(v_averagesales,'999,999,999.99'),29)||' |');
END LOOP;

CLOSE csr_top20custlist;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',109,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',59,' ') || ' ' ||
LPAD(' | ',48));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',109,'-'));
END;
/

exec prc_customerreport;

34
Results

3.3.3 Overall Vendor Analytic Report


The overall vendor report generated by PSMS will show us information about the vendors and the
products they are selling. It will also list the buy price of the product and the total sales and the total
number of orders the vendors have ordered. Finally, it will show the top 3 most popular products and rank
them accordingly.

The purpose of this report is to let the company know the expenses of the first 3 order from each of the
vendors. It can say that the higher the order purchased from the vendor, the higher the sales made by the
company.

For example, the highest 3 rank vendor's product in Autocart Studio Design is Ford Mustag that the sales
is RM14247193.68 followed by BMW R 1100 S, sales is RM8258656.00 and the third model is Model A
Ford J-Courpe, sales is RM9238664.23.

SQL Query
--Report Based on Vendors (overall)
set serveroutput on;
set linesize 200;
set pagesize 50;

DROP VIEW allvendorsView;


CREATE OR REPLACE VIEW allvendorsView AS

35
select productvendor as vendor,
concat(p.productcode,p.productname) as product, p.buyprice,
sum(s.linetotal) as totalsales, count(s.ordernumber) as orderquantity,
RANK() over(PARTITION BY productvendor order by
count(s.ordernumber) desc) as productrank
from sales_fact s
join dim_products p on p.product_key = s.product_key
group by productvendor, concat(p.productcode,p.productname),
p.buyprice;

CREATE OR REPLACE PROCEDURE prc_vendorreport IS


v_vendor dim_products.productvendor%TYPE;
v_product varchar2(100);
v_buyprice number(5,2);
v_totalsales number(13,2);
v_orderquantity number;
v_productrank number(2);

CURSOR csr_getvendortop3 IS
SELECT vendor, product, buyprice, totalsales, orderquantity,
productrank
FROM allvendorsView
where productrank <= 3;
BEGIN
OPEN csr_getvendortop3;

DBMS_OUTPUT.PUT_LINE(LPAD('=',140,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',84,' ')||LPAD(' | ',55));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Overall Vendor Analytic
Report',81,' ') || ' ' || LPAD(' | ',57));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',136) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,123, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',140,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Vendor Name',25)||' | '||
RPAD('Product Name',45)||' | '||LPAD('Buy Price (RM)',16)||
' | '||LPAD('No. of Order',12)||' | '||LPAD('Sales (RM)',17)||' |
'||LPAD('Rank',6)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',140,'-'));

LOOP
FETCH csr_getvendortop3 INTO v_vendor, v_product, v_buyprice,

36
v_totalsales, v_orderquantity, v_productrank;
EXIT WHEN csr_getvendortop3%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_vendor,25)|| ' | '||
RPAD(v_product,45)||' | '||
LPAD(to_char(v_buyprice,'999,999,999.99'),16)||
' | '||LPAD(v_orderquantity,12)||' | '||
LPAD(to_char(v_totalsales,'999,999,999.99'),17)||
' | '||LPAD(v_productrank,6)||' | ');
END LOOP;
CLOSE csr_getvendortop3;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',140,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',78,' ') || ' ' ||
LPAD(' | ',60));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',140,'-'));

END;
/

exec prc_vendorreport;

Results

37
3.4 Keoh Ji Xiang

3.4.1 Annual Report for 2021


The annual report generated for 2021 consists of the expenses, sales and profit of Popular Scale Model
Supplier’s (PSMS) sales in the year of 2021. In the generated annual report, it also shows PSMS’s sales
contribution in percentage. At the bottom of the annual report it will calculate and display the total
expenses, total sales, and total profit PSMS has generated in the year of 2021.

The purpose of this report being generated is to allow the users to have an overall view of the company’s
performance in 2021 combined into one report instead of needing them to view multiple reports to get the
same information.

For example, the product 1952 Alpine Renault 1300 was the number one product in 2021 and it costs the
company a total expense of RM1,103,110.20 in 2021 and achieve a total sale of RM2,398,017.00
resulting in a profit of RM1,121,695.92 and a sale contribution percentage of 2.25%.

SQL Query

Results
--ANNUAL REPORT OF 2021
set serveroutput on;
set linesize 200;
set pagesize 50;

drop procedure prc_annual_report;

CREATE OR REPLACE PROCEDURE prc_annual_report IS


-- variables declaration
v_productcode dim_products.productcode%TYPE;
v_productname dim_products.productname%TYPE;
v_expenses number(13,2);

38
v_sales number(13,2);
v_profit number(13,2);
v_contribution number(4,2);

v_sumexpenses number(13,2);
v_sumsales number(13,2);
v_sumprofit number(13,2);

CURSOR csr_getdata IS
select * from(
select productcode, productname, sum(p.buyprice *
s.quantityordered) as expenses,
sum(s.linetotal) as sales, sum(s.linetotal) -
sum(p.buyprice * s.quantityordered) as profit,
ROUND((sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) /
(select sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)
from sales_fact s join dim_products p on s.product_key
= p.product_key)*100,2) as contribution
from sales_fact s
join dim_date d on s.date_key = d.date_key
join dim_products p on s.product_key = p.product_key
where d.cal_year = '2021'
group by productcode, productname
order by (sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) /
(select sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)
from sales_fact s join dim_products p on s.product_key
= p.product_key) desc)
where rownum <=10;

CURSOR csr_gettotal IS
select sum(expenses), sum(sales), sum(profit) from(
select productcode, productname, sum(p.buyprice *
s.quantityordered) as expenses,
sum(s.linetotal) as sales, sum(s.linetotal) -
sum(p.buyprice * s.quantityordered) as profit,
ROUND((sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) /
(select sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)

39
from sales_fact s join dim_products p on s.product_key
= p.product_key)*100,2) as contribution
from sales_fact s
join dim_date d on s.date_key = d.date_key
join dim_products p on s.product_key = p.product_key
where d.cal_year = '2021'
group by productcode, productname
order by (sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) /
(select sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)
from sales_fact s join dim_products p on s.product_key
= p.product_key) desc)
where rownum <=10;

BEGIN
--OPEN csr_getdata(v_reportyear);
OPEN csr_getdata;

DBMS_OUTPUT.PUT_LINE(LPAD ('=',129,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',82,' ')||
LPAD(' | ',46));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Annual Report for the Year of
2021',82,' ') || ' ' || LPAD(' | ',45));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',124) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,113, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',129,'-'));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',129,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Product Code',12)||' | '||
RPAD('Product Name',25)||' | '||LPAD('Expenses (RM)',16)||' | '||
LPAD('Sales (RM)',16)||' | '||
LPAD('Profit (RM)',16)||' | '||LPAD('Profit Contribution
(%)',25)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',129,'-'));

LOOP
FETCH csr_getdata INTO v_productcode, v_productname,
v_expenses, v_sales, v_profit, v_contribution;
EXIT WHEN csr_getdata%NOTFOUND;

40
DBMS_OUTPUT.put_line('| '||RPAD(v_productcode,12)|| ' | '||
RPAD(v_productname,25)|| ' | ' ||
LPAD(to_char(v_expenses,'999,999,999.99'),16)
||' | ' || LPAD(to_char(v_sales,'999,999,999.99'),16)||' | '||
LPAD(to_char(v_profit,'999,999,999.99'),16)||
' | '||LPAD(to_char(v_contribution,'990.99'),25)||' | ');
END LOOP;
CLOSE csr_getdata;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',129,'-'));

OPEN csr_gettotal;
FETCH csr_gettotal INTO v_sumexpenses, v_sumsales, v_sumprofit;

--display total
DBMS_OUTPUT.PUT_LINE(LPAD ('*',129,'*'));
DBMS_OUTPUT.PUT_LINE('| ' || LPAD('Total Expenses in the Year of
2021 (RM): ',109,' ')|| to_char(v_sumexpenses,'999,999,999.99')
|| ' | ');
DBMS_OUTPUT.PUT_LINE('| ' || LPAD('Total Sales in the Year of 2021
(RM): ',109,' ')|| to_char(v_sumsales,'999,999,999.99')
|| ' | ');
DBMS_OUTPUT.PUT_LINE('| ' || LPAD('Total Profit in the Year of
2021 (RM): ',109,' ')|| to_char(v_sumprofit,'999,999,999.99')
|| ' | ');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',129,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',70,' ') || ' ' ||
LPAD(' | ',57));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',129,'-'));

CLOSE csr_gettotal;
END;
/

set serveroutput on;


exec prc_annual_report;

41
3.4.2 Monthly Performance Report
The monthly report will contain information such as the profit obtained each month, and the profit of the
previous month so that the profit ratio can be calculated and be displayed in percentage. Finally it will
also show the total order count from each month

The purpose of this report is to show the user a more detailed monthly report as compared to the annual
report. The report breaks down the annual sales into months for a more detailed look at the company
sales.

For example, the sales in February had a total profit of RM27,460,660.81. And the profit in the previous
month, January, had a total profit of RM36,020,464.38 resulting in a profit ratio of 76% which was a 24%
decline. February has a total order count of 108065 while the total order in January is 141501.

SQL Query
--Monthly Performance for the year of 2021
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE PROCEDURE prc_monthlyreport IS


v_month number(3);
v_month2 varchar(20);
v_profit number(15,2);
v_profitprev number(15,2);
v_profitratio number(15,2);
v_count number;

CURSOR csr_getmonthly IS

42
select d.cal_month_no_in_year as month, sum(s.linetotal) -
sum(p.buyprice * s.quantityordered) as profit,
coalesce(LAG(sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) OVER ( ORDER BY d.cal_month_no_in_year ),0) AS
Profit_Previous_Month,--Lag is use to delay one row
--cal profit ratio based on previous year formula(profit
this year / profit previous year)
coalesce((round((sum(s.linetotal) - sum(p.buyprice *
s.quantityordered))/
(LAG(sum(s.linetotal) - sum(p.buyprice *
s.quantityordered)) OVER ( ORDER BY d.cal_month_no_in_year )),2)),0)
AS profit_ratio,
--cal count of record for the particular year
count(s.ordernumber) as countOfRecord
from sales_fact s
join dim_date d on s.date_key = d.date_key
join dim_products p on s.product_key = p.product_key
group by d.cal_month_no_in_year
order by d.cal_month_no_in_year;
BEGIN
OPEN csr_getmonthly;

DBMS_OUTPUT.PUT_LINE(LPAD ('=',103,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',70,' ')||
LPAD(' | ',32));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Monthly Performance for the Year
of 2021',73,' ') || ' ' || LPAD(' | ',28));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',99) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,89, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',103,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Month',12)||' | '||LPAD('Profit
(RM)',16)||' | '||LPAD('Profit of Previous Month (RM)',31)||' | '||
LPAD('Profit Ratio',14)||' | '||LPAD('No. of Order',14)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',103,'-'));

LOOP
FETCH csr_getmonthly INTO v_month, v_profit, v_profitprev,
v_profitratio, v_count;
EXIT WHEN csr_getmonthly%NOTFOUND;

IF v_month = 1 THEN

43
v_month2 := 'January';
ELSIF v_month = 2 THEN
v_month2 := 'February';
ELSIF v_month = 3 THEN
v_month2 := 'March';
ELSIF v_month = 4 THEN
v_month2 := 'April';
ELSIF v_month = 5 THEN
v_month2 := 'May';
ELSIF v_month = 6 THEN
v_month2 := 'June';
ELSIF v_month = 7 THEN
v_month2 := 'July';
ELSIF v_month = 8 THEN
v_month2 := 'August';
ELSIF v_month = 9 THEN
v_month2 := 'September';
ELSIF v_month = 10 THEN
v_month2 := 'October';
ELSIF v_month = 11 THEN
v_month2 := 'November';
ELSIF v_month = 12 THEN
v_month2 := 'December';
END IF;

DBMS_OUTPUT.put_line('| '||RPAD(v_month2,12)|| ' | ' ||


LPAD(to_char(v_profit,'999,999,990.99'),16)|| ' | ' ||
LPAD(to_char(v_profitprev,'999,999,990.99'),31)
||' | '|| LPAD(to_char(v_profitratio,'990.90'),14)||' | '||
LPAD(to_char(v_count,'999,999,990'),14)||' |');
END LOOP;
CLOSE csr_getmonthly;

DBMS_OUTPUT.PUT_LINE(LPAD ('-',103,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',58,' ') || ' ' ||
LPAD(' | ',43));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',103,'-'));
END;
/

exec prc_monthlyreport;

Results

44
3.4.3 Customer Country Analysis
The customer country analysis report will contain the information regarding where PSMS’s customers are
from and the top 3 highest sale customers. It will display the top 3 customer sales in each of the country
PSMS’s customers are from. The report will also show the amount of each sale and the ranking of the sale
in that specific country.

Based on the report, it can help the user to focus the sales on a certain country. For example, the user can
maintain the customer which has higher potential to avoid customer churn. So, by fulfilling the
customers’ request, users can refer to this report to make better decisions and plan better strategies to
attract more customers based on their requirement.

For example, in Australia, Australian Coll07187 has the highest sales which is RM88,844 and followed
by Souvenirs And 114,332 and 105,426 which is RM87,035 and RM81955.

45
SQL Query
--Analysis Based on Customer Country
set serveroutput on;
set linesize 200;
set pagesize 50;

CREATE OR REPLACE VIEW customercountryView AS


select c.country, sum(s.linetotal) as sales, c.customername as
customerhighest,
RANK() over(PARTITION BY country order by sum(s.linetotal) desc)
as custrank
from sales_fact s
join dim_customers c on c.customer_key = s.customer_key
group by country, c.customername;

CREATE OR REPLACE PROCEDURE prc_analysiscustomercountry IS


-- variables declaration
v_country dim_customers.country%TYPE;
v_customername varchar2(50);
v_linetotal number(15,2);
v_customerrank number;

--get country ranking and cuntry sales


CURSOR csr_getsalesbycountry IS
SELECT country,customerhighest, sales, custrank
FROM customercountryView
where custrank <= 3;

BEGIN
DBMS_OUTPUT.PUT_LINE(LPAD ('=',80,'='));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Popular Scale Model Supplier
(PSMS)',55,' ')||
LPAD(' | ',24));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('Analysis Based on Customer
Country',55,' ') || ' ' || LPAD(' | ',23));
DBMS_OUTPUT.PUT_LINE('| '|| RPAD(' ',76) ||' | ');
DBMS_OUTPUT.PUT_LINE('| '|| LPAD('Report Generated:' ,66, ' ')||
TO_DATE(SYSDATE, 'DD-MON-YYYYHH24:MI:SS') || ' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',80,'-'));

--for header
DBMS_OUTPUT.PUT_LINE('| '||RPAD('Country',16)||' | '||RPAD('TOP 3
Highest Sale Customer',30)||' | '||LPAD('Sales (RM)',15)||' | '||
LPAD('Rank',6)||' |');
DBMS_OUTPUT.PUT_LINE(LPAD ('-',80,'-'));

46
OPEN csr_getsalesbycountry;

LOOP
FETCH csr_getsalesbycountry INTO v_country, v_customername,
v_linetotal, v_customerrank;
EXIT WHEN csr_getsalesbycountry%NOTFOUND;
DBMS_OUTPUT.put_line('| '||RPAD(v_country,16)|| ' | ' ||
RPAD(v_customername,30)||' | ' ||
LPAD(to_char(v_linetotal,'999,999,999'),15)||' | '||
LPAD(v_customerrank,6)||' |');
END LOOP;

CLOSE csr_getsalesbycountry;
DBMS_OUTPUT.PUT_LINE(LPAD ('-',80,'-'));
DBMS_OUTPUT.PUT_LINE('| '||LPAD('End Of Report',45,' ') || ' ' ||
LPAD(' | ',33));
DBMS_OUTPUT.PUT_LINE(LPAD ('-',80,'-'));
END;
/

exec prc_analysiscustomercountry;

47
Results

48
49
50

You might also like