SQL Analysis and Tableau Vizualization for Courierly company
About the company: Courierly is a company that
connects vendors to drivers for delivering packages. A vendor can
place an order request for a package pick up, and drivers that are
employed by the Courierly app can then accept or reject the order
request. Vendors can either be larger corporate entities, small
businesses, or individuals.
Objective: To analyze driver performance and
engagement as well as vendor and order insights.
Applied skills: Excel
SQL SQLite
Tableau
Stakeholder questions:
-
Which 3 drivers have completed the most orders over the last 6
months of 2024?
-
How do payouts compare for drivers during promotion periods versus
non-promotion periods?
-
How many drivers joined in the 2024, and how many of them are
still active? A driver is active if they have completed any orders
within the last 4 months of 2024.
-
Do drivers with Courierly stickers in their cars (Sticker = Y)
complete more orders than those without?
-
Which type of vendors (Corporate vs. Personal) generates the most
revenue for Courierly?
-
What is the average total distance per order, and how does it
differ between Corporate and Personal vendors?
-
What percentage of orders are canceled or rejected, and are there
any patterns based on vendor type or order distance?
Answers
Which 3 drivers have completed the most orders over the last 6
months of 2024?
Over the past 6 months from today (December 29, 2024), the top 3
drivers who completed the most orders are:
- Joshua Oconnor - 26 orders,
- Cody Wilson - 25 orders,
- Lauren Jordan - 25 orders,
SQL query
SELECT d.driverid, d.name, COUNT(o.OrderID) AS [Orders Number]
FROM Drivers d
LEFT JOIN Order_Status os ON os.DriverID = d.DriverID
LEFT JOIN Orders o ON o.OrderID = os.OrderID
WHERE os.OrderStatus = 'Completed'
AND o.Order_Date BETWEEN datetime('2024-12-31', 'start of month', '+30 days', '-6 month')
AND date('2024-12-31')
GROUP BY d.driverid
ORDER BY COUNT(o.OrderID) DESC
How do payouts compare for drivers during promotion periods versus
non-promotion periods?
Payouts were compared among drivers who participated in both promo and
regular periods based on average payout data for each period. The
results were ordered based on the highest payout during the promo
period. The driver with the highest payout earned 172.69% more during
the promo period than the regular. The driver with the lowest payout
made 82.71% less during the promo period than the regular. On average,
drivers earned 10.24% more during the promo period than regular.
SQL query
/* Average single value for promo and regular periods and percentage change between payouts */
select round(avg(avg_promo_payout),2) as avg_promo_payout, round(avg(avg_reg_payout),2) as avg_regular_payout, round(100.0 * (avg(avg_promo_payout) - avg(avg_reg_payout)) / avg(avg_reg_payout),2) || '%' as change_percentage from (select distinct p.DriverID,
/* Average payout values for each period and percentage change per driver */
round(avg(iif(o.order_date Between pr.Promotion_Begin and pr.Promotion_End and os.OrderStatus = 'Completed', p.price_payout, null)),2) as avg_promo_payout,
round(avg(iif(o.order_date < pr.Promotion_Begin or o.order_date > pr.Promotion_End and os.OrderStatus = 'Completed', p.price_payout, null)),2) as avg_reg_payout,
round(100.0 * (avg(iif(o.order_date Between pr.Promotion_Begin and pr.Promotion_End and os.OrderStatus = 'Completed', p.price_payout, null)) - avg(iif(o.order_date < pr.Promotion_Begin or o.order_date > pr.Promotion_End and os.OrderStatus = 'Completed', p.price_payout, null)))/avg(iif(o.order_date < pr.Promotion_Begin or o.order_date > pr.Promotion_End and os.OrderStatus = 'Completed', p.price_payout, null)),2) || '%' as change_percentage
from payout p
inner join promotions pr on pr.DriverID = p.DriverID
inner join orders o on o.OrderID = p.OrderID
inner join Order_Status os on os.OrderID = p.OrderID
group by p.DriverID
having avg_promo_payout is not null and avg_reg_payout is not null
order by avg_promo_payout desc)
How many drivers joined in the 2024, and how many of them are still
active? A driver is active if they have completed any orders within
the last 4 months of 2024.
During the last 2024 year, 461 drivers joined Courierly. All of them
are still active in the company, as they have completed orders within
the last 4 months.
SQL Query
with drivers_joined_2024 as (select driverid, name, count(driverid) over (partition by strftime('%Y', date_of_joining)) as joined_2024 from Drivers
where strftime('%Y', date_of_joining) = '2024')
select count(distinct d.driverid) from drivers_joined_2024 d
left join Order_Status os on os.driverid = d.driverid
left Join Orders o on o.OrderID = os.OrderID
where o.order_date >= datetime('2024-12-31','start of month', '+30 days', '-4 month') and os.OrderStatus = 'Completed'
Do drivers with Courierly stickers in their cars (Sticker = Y)
complete more orders than those without?
Yes, drivers with company stickers complete more orders than those
without. For reference, drivers without stickers completed 22496
orders compared to the drivers with stickers. The latter completed
26129 orders, which is 3633 more orders than those without stickers.
SQL Query
with stickers as (select count(iif(d.[Sticker_(Y/N)] = 'Y', os.OrderID, null)) as orders_with_stickers, count(iif(d.[Sticker_(Y/N)] = 'N', os.OrderID, null)) as orders_without_stickers from Order_Status os
left join Drivers d on d.DriverID = os.DriverID
where os.OrderStatus = 'Completed')
select orders_with_stickers, orders_without_stickers, (orders_with_stickers - orders_without_stickers) as difference from stickers
Which type of vendors (Corporate vs. Personal) generates the most
revenue for Courierly?
Corporate vendors generate more revenue than personal ones. Personal
vendors generated 2831441.03, which is 22.23% of the total revenue,
while corporate - 9908430.55, which is 77.77% of the total revenue.
SQL Query
with sorted_vendors as (select sum(iif(v.Vendor_Type = 'Personal', Price, null)) as personal_total_revenue, sum(iif(v.Vendor_Type = 'Corporate', Price, null)) as corporate_total_revenue from Vendors v
left join Orders o on o.VendorID = v.VendorID)
select personal_total_revenue, round(personal_total_revenue/(personal_total_revenue + corporate_total_revenue) * 100,2) || '%' as personal_percentage, corporate_total_revenue, round(corporate_total_revenue/(personal_total_revenue + corporate_total_revenue) * 100,2) || '%' as corporate_percentage from sorted_vendors
What is the average total distance per order, and how does it differ
between Corporate and Personal vendors?
The average distance per order from personal vendors is 126.41, while
from corporate vendors - 126.5. This minor difference shows that
distance does not depend on the vendor type.
SQL Query
with avg_distance as (select avg(iif(v.Vendor_Type = 'Personal',o.total_distance, null)) as personal_average_distance, avg(iif(v.Vendor_Type = 'Corporate',o.total_distance, null)) as corporate_average_distance from Orders o
left join Vendors v on v.VendorID = o.VendorID)
select round(personal_average_distance, 2) as personal, round(corporate_average_distance,2) as corporate from avg_distance
What percentage of orders are canceled or rejected, and are there
any patterns based on vendor type or order distance?
The total percentage of canceled or rejected orders is 44.02%, which
is slightly less than half of all orders. Among them, the cancellation
percentage from corporate vendors is 78.08%, and personal - 21.92%.
Based on the distance, the amount of canceled orders with a distance
range from minimal to average makes 49.45%, and from average to
maximal - 50.55%. Based on the above calculations, the largest part of
cancellations/rejections comes from orders from corporate vendors
(almost 80%). Distance doesn’t affect cancellations/rejections
significantly, as the percentage of minimal to average and average to
maximal distances is almost equal.
SQL Query
/* Number of canceled orders */
with cancelled_orders as (select o.OrderID, o.Total_Distance, o.VendorID from Orders o
left join Order_Status os on os.OrderID = o.OrderID
where os.OrderStatus in ('Cancelled', 'Rejected')),
/* The total percentage of cancelled/rejected orders*/
percentage_of_cancellations as (select round(100.0 * (count(co.OrderID))/(select count(orderid) from Orders),2) || '%' from cancelled_orders co)
/* Percentage of cancellations among canceled orders based on different criteria */
select round(100.0 * count(iif(v.Vendor_Type = 'Corporate', o.orderid, null))/ count(o.OrderID), 2) || '%' as percent_corporate,
round(100.0 * count(iif(v.Vendor_Type = 'Personal', o.orderid, null))/ count(o.OrderID), 2) || '%' as percent_personal,
round(100.0 * count(iif(o.Total_Distance <= (select avg(total_distance) from Orders), o.orderid, null))/ count(o.OrderID), 2) || '%' as min_to_avg_distance,
round(100.0 * count(iif(o.Total_Distance > (select avg(total_distance) from Orders), o.orderid, null))/ count(o.OrderID), 2) || '%' as avg_to_max_distance from cancelled_orders o
left join Vendors v on v.VendorID = o.VendorID