Compare commits

...

10 Commits

34 changed files with 893 additions and 10 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,6 @@
# MySQL Workbench project backup files
*.mwb.bak
# Python
.ipynb_checkpoints
__pycache__

View File

@@ -19,7 +19,7 @@ USE `LittleLemonDB` ;
-- Table `LittleLemonDB`.`Addresses`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`Addresses` (
`AddressID` INT NOT NULL,
`AddressID` INT NOT NULL AUTO_INCREMENT,
`Street` VARCHAR(255) NOT NULL,
`PostalCode` VARCHAR(12) NOT NULL,
`City` VARCHAR(255) NOT NULL,
@@ -32,7 +32,7 @@ ENGINE = InnoDB;
-- Table `LittleLemonDB`.`Customers`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`Customers` (
`CustomerID` INT NOT NULL,
`CustomerID` INT NOT NULL AUTO_INCREMENT,
`FirstName` VARCHAR(255) NOT NULL,
`LastName` VARCHAR(255) NOT NULL,
`PhoneNumber` VARCHAR(45) NOT NULL,
@@ -52,7 +52,7 @@ ENGINE = InnoDB;
-- Table `LittleLemonDB`.`Employees`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`Employees` (
`EmployeeID` INT NOT NULL,
`EmployeeID` INT NOT NULL AUTO_INCREMENT,
`FirstName` VARCHAR(255) NOT NULL,
`LastName` VARCHAR(255) NOT NULL,
`PhoneNumber` VARCHAR(45) NOT NULL,
@@ -74,7 +74,7 @@ ENGINE = InnoDB;
-- Table `LittleLemonDB`.`Bookings`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`Bookings` (
`BookingID` INT NOT NULL,
`BookingID` INT NOT NULL AUTO_INCREMENT,
`Reserved` DATETIME NOT NULL,
`TableNumber` INT NOT NULL,
`CustomerID` INT NOT NULL,
@@ -99,7 +99,7 @@ ENGINE = InnoDB;
-- Table `LittleLemonDB`.`MenuItems`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`MenuItems` (
`MenuItemID` INT NOT NULL,
`MenuItemID` INT NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(255) NOT NULL,
`Category` VARCHAR(255) NOT NULL,
`Price` DECIMAL(6,2) NOT NULL,
@@ -112,7 +112,7 @@ ENGINE = InnoDB;
-- Table `LittleLemonDB`.`Orders`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`Orders` (
`OrderID` INT NOT NULL,
`OrderID` INT NOT NULL AUTO_INCREMENT,
`Started` DATETIME NOT NULL,
`Finished` DATETIME NULL,
`BillAmount` DECIMAL(10,2) NULL,
@@ -131,7 +131,7 @@ ENGINE = InnoDB;
-- Table `LittleLemonDB`.`OrderItems`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `LittleLemonDB`.`OrderItems` (
`OrderItemID` INT NOT NULL,
`OrderItemID` INT NOT NULL AUTO_INCREMENT,
`Ordered` DATETIME NOT NULL,
`Delivered` DATETIME NULL,
`DeliveryStatus` VARCHAR(45) NOT NULL,

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
analysis/data_source.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

View File

@@ -2,11 +2,11 @@ name: capstone-project
channels:
- conda-forge
dependencies:
- pip
- python=3.13
- pynvim
- pytest
- pip
- jupyter
- mysql-connector-python
- pynvim
- pip:
- ruff
- basedpyright

216
promotional_campaign.ipynb Normal file
View File

@@ -0,0 +1,216 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3228e84e-083c-48f6-9913-9d96e2fd0a2a",
"metadata": {},
"source": [
"# Promotional campaign"
]
},
{
"cell_type": "markdown",
"id": "71a559ef-75a7-4aca-86aa-10629e047222",
"metadata": {},
"source": [
"## Imports"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8fe32f7f-09b3-4d9a-af26-4146c500bf44",
"metadata": {},
"outputs": [],
"source": [
"from decimal import Decimal\n",
"from mysql.connector import CMySQLConnection, Error, connect\n",
"from mysql.connector.cursor_cext import CMySQLCursor"
]
},
{
"cell_type": "markdown",
"id": "70da0c3b-ae18-4b60-a7eb-1c0c75c3677a",
"metadata": {},
"source": [
"## Task 1"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ac95c33d-3ead-4dab-b007-4f20d37f7386",
"metadata": {},
"outputs": [],
"source": [
"connection = connect(\n",
" user=\"tobias\",\n",
" unix_socket=\"/run/mysqld/mysqld.sock\",\n",
" database=\"LittleLemonDB\",\n",
" use_pure=False,\n",
" autocommit=True,\n",
")\n",
"assert isinstance(connection, CMySQLConnection)"
]
},
{
"cell_type": "markdown",
"id": "19e74d2d-2b40-4a1b-a57e-38f27106220b",
"metadata": {},
"source": [
"## Task 2"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1b386dae-78e4-44db-90e5-6da378de16fe",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Tables:\n",
" Addresses\n",
" Bookings\n",
" Customers\n",
" Employees\n",
" MenuItems\n",
" OrderItems\n",
" Orders\n"
]
}
],
"source": [
"try:\n",
" with connection.cursor() as cursor:\n",
" assert isinstance(cursor, CMySQLCursor)\n",
"\n",
" _ = cursor.execute(\"show tables\")\n",
" print(\"Tables:\")\n",
" for (table_name,) in cursor:\n",
" print(f\" {table_name}\")\n",
"except Error as err:\n",
" print(err)"
]
},
{
"cell_type": "markdown",
"id": "62e2485b-1391-4fc6-aa73-35d0ebd8e95d",
"metadata": {},
"source": [
"## Task 3\n",
"**SQL query in `sql/promotional_campaign.sql`:**\n",
"```sql\n",
"select\n",
" C.FirstName,\n",
" C.LastName,\n",
" C.PhoneNumber,\n",
" C.EmailAddress,\n",
" max(O.BillAmount) as MaxBillAmount\n",
"from Orders as O\n",
"inner join Bookings as B on O.BookingID = B.BookingID\n",
"inner join Customers as C on B.CustomerID = C.CustomerID\n",
"group by C.CustomerID\n",
"having MaxBillAmount > 60.00;\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "214d0b0e-dd5b-4113-b6d7-aca020655ef3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Customers that placed an order greater then 60.00€:\n",
" Li Wei Zhang\n",
" Minimum bill amount: 82.50€\n",
" Phone number: +86123456789\n",
" Email address: li.wei.zhang@example.com\n",
" Ivanov Petrov\n",
" Minimum bill amount: 150.50€\n",
" Phone number: +70987654321\n",
" Email address: ivanov.petrov@example.com\n"
]
}
],
"source": [
"with open(\"./sql/promotional_campaign.sql\") as promo_campaign_file:\n",
" promo_campain_request = promo_campaign_file.read()\n",
" \n",
"try:\n",
" with connection.cursor() as cursor:\n",
" assert isinstance(cursor, CMySQLCursor)\n",
" _ = cursor.execute(promo_campain_request)\n",
"\n",
" print()\n",
"\n",
" print(\"Customers that placed an order greater then 60.00€:\")\n",
" for (\n",
" first_name,\n",
" last_name,\n",
" phone_number,\n",
" email_address,\n",
" min_bill_amount,\n",
" ) in cursor:\n",
" assert isinstance(first_name, str)\n",
" assert isinstance(last_name, str)\n",
" assert isinstance(phone_number, str)\n",
" assert isinstance(email_address, str | None)\n",
" assert isinstance(min_bill_amount, Decimal)\n",
" print(f\" {first_name} {last_name}\")\n",
" print(f\" Minimum bill amount: {min_bill_amount}€\")\n",
" print(f\" Phone number: {phone_number}\")\n",
" if email_address:\n",
" print(f\" Email address: {email_address}\")\n",
"except Error as err:\n",
" print(err)"
]
},
{
"cell_type": "markdown",
"id": "be3c702d-5ab6-47a6-a19b-4e26aefdbd6e",
"metadata": {},
"source": [
"## Cleanup"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e7f0a6dc-4940-4f8b-a89c-e714e9d375ba",
"metadata": {},
"outputs": [],
"source": [
"connection.close()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

19
pyproject.toml Normal file
View File

@@ -0,0 +1,19 @@
[tool.sqlfluff.core]
dialiact = "mysql"
max_line_length = 110
exclude_rules = "ST06"
[tool.sqlfluff.rules.capitalisation.keywords]
capitalisation_policy = "lower"
[tool.sqlfluff.rules.capitalisation.identifiers]
extended_capitalisation_policy = "pascal"
[tool.sqlfluff.rules.capitalisation.types]
extended_capitalisation_policy = "lower"
[tool.sqlfluff.rules.references.keywords]
ignore_words = "role"
[tool.sqlfluff.indentation]
allow_implicit_indents = true

15
sql/add_booking.sql Normal file
View File

@@ -0,0 +1,15 @@
-- modify bookings - task 1
-- changes:
-- changed BookingDate (Reserved) data type from date to datetime, because I assumed that Little Lemon
-- allows wants bookings per day and table - a table is considered blocked for 2 hours
-- added EmployeeID input parameter
-- noqa: disable=CP03,LT02,RF03
create procedure AddBooking (
in BookingID int, in Reserved datetime, in TableNumber int, in CustomerID int, in EmployeeID int
)
begin
insert into Bookings (BookingID, Reserved, TableNumber, CustomerID, EmployeeID) values (
BookingID, Reserved, TableNumber, CustomerID, EmployeeID
);
select concat("New booking added") as Confirmation;
end

30
sql/add_valid_booking.sql Normal file
View File

@@ -0,0 +1,30 @@
-- available bookings - task 3
-- changes:
-- changed BookingDate (Reserved) data type from date to datetime, because I assumed that Little Lemon
-- allows wants bookings per day and table - a table is considered blocked for 2 hours
-- added CustomerID and EmployeeID input parameters
-- noqa: disable=CP03,LT02,RF03
--
create procedure AddValidBooking (
in Reserved datetime, in TableNumber int, in CustomerID int, in EmployeeID int
)
begin
start transaction;
if TableIsFree(Reserved, TableNumber) then
insert into Bookings (Reserved, TableNumber, CustomerID, EmployeeID) values (
Reserved, TableNumber, CustomerID, EmployeeID
);
commit;
select concat(
"Table ", TableNumber,
" was free - booking accepted (BookingID=", (select last_insert_id()), ')'
) as BookingStatus;
else
rollback;
select concat(
"Table ", TableNumber,
" is already booked - booking cancelled"
) as BookingStatus;
end if;
end

View File

@@ -0,0 +1,97 @@
# Task 1
> source ./example_bookings.sql
> select * from `Bookings` where `BookingID` between 11 and 14
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 11 | 2022-10-14 18:00:00 | 5 | 1 | 1 |
| 12 | 2022-11-12 19:30:00 | 3 | 3 | 1 |
| 13 | 2022-10-11 18:45:00 | 2 | 2 | 1 |
| 14 | 2022-10-13 19:15:00 | 2 | 1 | 1 |
+-----------+---------------------+-------------+------------+------------+
# Task 2
> delimiter $
> source ./table_is_free.sql
> source ./check_booking.sql
> delimiter ;
> call CheckBooking("2022-11-12 18:15:00", 3, @IsFree);
+---------------------------+
| BookingStatus |
+---------------------------+
| Table 3 is already booked |
+---------------------------+
> select @IsFree;
+---------+
| @IsFree |
+---------+
| 0 |
+---------+
> call CheckBooking("2022-11-12 17:15:00", 3, @IsFree);
+-----------------+
| BookingStatus |
+-----------------+
| Table 3 is free |
+-----------------+
> select @IsFree;
+---------+
| @IsFree |
+---------+
| 1 |
+---------+
# Task 3
> delimiter $
> source ./add_valid_booking.sql
> delimiter ;
> call AddValidBooking("2022-11-12 18:15:00", 3, 4, 1);
+-----------------------------------------------+
| BookingStatus |
+-----------------------------------------------+
| Table 3 is already booked - booking cancelled |
+-----------------------------------------------+
> select * from `Bookings`
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 1 | 2024-11-14 18:00:00 | 1 | 3 | 1 |
| 2 | 2024-11-14 19:30:00 | 2 | 2 | 1 |
| 3 | 2024-11-14 18:45:00 | 3 | 4 | 1 |
| 4 | 2024-11-14 19:15:00 | 4 | 1 | 2 |
| 5 | 2024-11-14 18:30:00 | 5 | 6 | 2 |
| 6 | 2024-11-14 19:00:00 | 6 | 5 | 2 |
| 7 | 2024-11-15 18:15:00 | 1 | 7 | 5 |
| 8 | 2024-11-15 19:45:00 | 2 | 8 | 5 |
| 9 | 2024-11-15 18:30:00 | 3 | 9 | 2 |
| 10 | 2024-11-15 19:00:00 | 4 | 10 | 5 |
| 11 | 2022-10-14 18:00:00 | 5 | 1 | 1 |
| 12 | 2022-11-12 19:30:00 | 3 | 3 | 1 |
| 13 | 2022-10-11 18:45:00 | 2 | 2 | 1 |
| 14 | 2022-10-13 19:15:00 | 2 | 1 | 1 |
+-----------+---------------------+-------------+------------+------------+
> call AddValidBooking("2022-11-12 17:15:00", 3, 4, 1);
+----------------------------------------------------+
| BookingStatus |
+----------------------------------------------------+
| Table 3 was free - booking accepted (BookingID=15) |
+----------------------------------------------------+
> select * from `Bookings`
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 1 | 2024-11-14 18:00:00 | 1 | 3 | 1 |
| 2 | 2024-11-14 19:30:00 | 2 | 2 | 1 |
| 3 | 2024-11-14 18:45:00 | 3 | 4 | 1 |
| 4 | 2024-11-14 19:15:00 | 4 | 1 | 2 |
| 5 | 2024-11-14 18:30:00 | 5 | 6 | 2 |
| 6 | 2024-11-14 19:00:00 | 6 | 5 | 2 |
| 7 | 2024-11-15 18:15:00 | 1 | 7 | 5 |
| 8 | 2024-11-15 19:45:00 | 2 | 8 | 5 |
| 9 | 2024-11-15 18:30:00 | 3 | 9 | 2 |
| 10 | 2024-11-15 19:00:00 | 4 | 10 | 5 |
| 11 | 2022-10-14 18:00:00 | 5 | 1 | 1 |
| 12 | 2022-11-12 19:30:00 | 3 | 3 | 1 |
| 13 | 2022-10-11 18:45:00 | 2 | 2 | 1 |
| 14 | 2022-10-13 19:15:00 | 2 | 1 | 1 |
| 15 | 2022-11-12 17:15:00 | 3 | 4 | 1 |
+-----------+---------------------+-------------+------------+------------+

13
sql/cancel_booking.sql Normal file
View File

@@ -0,0 +1,13 @@
-- modify bookings - task 3
-- add and update bookings - task 3
-- noqa: disable=CP03,LT02,RF03
create procedure CancelBooking (in BookingID int)
begin
delete from Bookings as B
where B.BookingID = BookingID;
if row_count() = 1 then
select concat("Booking ", BookingID, " cancelled") as Confirmation;
else
select concat("Booking ", BookingID, " not cancelled - missing booking") as Confirmation;
end if;
end

9
sql/cancel_order.sql Normal file
View File

@@ -0,0 +1,9 @@
-- query optimization - task 3
-- noqa: disable=CP03
create procedure CancelOrder (in TargetOrderID int)
begin
delete from Orders
where OrderID = TargetOrderID;
select concat("Order ", TargetOrderID, if(row_count() = 1, " is cancelled", " doesn't exist")) as Confirmation;
end

10
sql/check_booking.sql Normal file
View File

@@ -0,0 +1,10 @@
-- available bookings - task 2
-- changes:
-- changed BookingDate (Reserved) data type from date to datetime, because I assumed that Little Lemon
-- allows wants bookings per day and table - a table is considered blocked for 2 hours
-- noqa: disable=CP03,LT02,RF03
create procedure CheckBooking (in Reserved datetime, in TableNumber int, out IsFree boolean) --
begin
set IsFree = TableIsFree(Reserved, TableNumber);
select concat("Table ", TableNumber, " is ", if(IsFree, "free", "already booked")) as BookingStatus;
end

View File

@@ -0,0 +1,15 @@
-- sales report - task 1
-- changes:
-- interpreted quantity as the number of ordered items, because I assumed that Little lemon wants
-- orders à la carte instead of fixed menus
drop view if exists OrdersView;
create view OrdersView as
select
O.OrderID,
count(OI.MenuItemID) as Quantity,
O.BillAmount as Cost
from Orders as O
inner join OrderItems as OI on O.OrderID = OI.OrderID
group by O.OrderID
having Quantity > 2;

12
sql/example_bookings.sql Normal file
View File

@@ -0,0 +1,12 @@
-- available bookings - task 1
-- changes:
-- changed BookingDate (Reserved) data type from date to datetime, because I assumed that Little Lemon
-- allows wants bookings per day and table - a table is considered blocked for 2 hours
-- changed BookingID range from 1-4 to 11-14, because there are already 10 Bookings in my test data set
-- added arbitrary EmployeeID that corresponds to the Employee that handled the booking process
-- noqa: disable=CP03
insert into Bookings (BookingID, Reserved, TableNumber, CustomerID, EmployeeID) values
(11, '2022-10-14 18:00:00', 5, 1, 1),
(12, '2022-11-12 19:30:00', 3, 3, 1),
(13, '2022-10-11 18:45:00', 2, 2, 1),
(14, '2022-10-13 19:15:00', 2, 1, 1);

View File

@@ -0,0 +1,47 @@
-- sales report - task 2
-- changes:
-- removed menu name, because I assumed that Little lemon wants orders à la carte instead of fixed menus
-- changed cost cutoff from 150.00 to 75.00, because I wanted more than one test result
with
GroupedOrderItems as (
select
OI.OrderID,
MI.Category,
concat(MI.Name, " (", count(OI.OrderItemID), "x)") as Item
from OrderItems as OI
inner join MenuItems as MI on OI.MenuItemID = MI.MenuItemID
group by OI.OrderID, MI.Category, OI.MenuItemID
),
MainOrderItems as (
select
OrderID,
group_concat(Item separator '\n') as Items
from GroupedOrderItems
where Category = "Main"
group by OrderID
),
StarterOrderItems as (
select
OrderID,
group_concat(Item separator '\n') as Items
from GroupedOrderItems
where Category = "Starter"
group by OrderID
)
select
C.CustomerID,
concat(C.FirstName, " ", C.LastName) as FullName,
O.OrderID,
O.BillAmount as Cost,
coalesce(MOI.Items, "") as Mains,
coalesce(SOI.Items, "") as Starters
from Orders as O
inner join Bookings as B on O.BookingID = B.BookingID
inner join Customers as C on B.CustomerID = C.CustomerID
left join MainOrderItems as MOI on O.OrderID = MOI.OrderID
left join StarterOrderItems as SOI on O.OrderID = SOI.OrderID
where O.BillAmount > 75.00
group by O.OrderID;

12
sql/get_max_quantity.sql Normal file
View File

@@ -0,0 +1,12 @@
-- query optimization - task 1
-- changes:
-- interpreted quantity as the number of items in an order, because I assumed that Little lemon wants
-- orders à la carte instead of fixed menus
create procedure GetMaxQuantity () -- noqa: disable=CP03
select count(OI.OrderItemID) as MaxQuantityInOrder
from MenuItems as MI
inner join OrderItems as OI on MI.MenuItemID = OI.MenuItemID
group by OI.OrderID
order by MaxQuantityInOrder desc
limit 1

15
sql/get_order_detail.sql Normal file
View File

@@ -0,0 +1,15 @@
-- query optimization - task 2
-- changes:
-- interpreted quantity as the number of items in an order, because I assumed that Little lemon wants
-- orders à la carte instead of fixed menus
prepare GetOrderDetail from "
select
O.OrderID,
count(OI.MenuItemID) as Quantity,
O.BillAmount as Cost
from Orders as O
inner join OrderItems as OI on O.OrderID = OI.OrderID
inner join Bookings as B on O.BookingID = B.BookingID
where B.CustomerID = ?;
";

View File

@@ -0,0 +1,11 @@
-- sales report - task 3
-- changes:
-- replaced menu name with menu item name, because I assumed that Little lemon wants orders à la carte
-- instead of fixed menus
select
MI.Name,
count(OI.OrderItemID) as OrderQuantity
from MenuItems as MI
inner join OrderItems as OI on MI.MenuItemID = OI.MenuItemID
group by OI.MenuItemID
having OrderQuantity > 2;

126
sql/init_data.sql Normal file
View File

@@ -0,0 +1,126 @@
delete from MenuItems;
insert into MenuItems (MenuItemID, Name, Category, Price) values
(1, "Grilled Artichokes", "Starter", 6.00),
(2, "Lentil Soup", "Starter", 7.00),
(3, "Humus Plate", "Starter", 6.00),
(4, "Eggplant Bruschetta", "Starter", 7.00),
(5, "Gigantes Plaki", "Main", 14.00),
(6, "Minestrone", "Main", 16.00),
(7, "Moussaka", "Main", 17.00),
(8, "Pasta alla Norma", "Main", 12.00),
(9, "Tabbouleh", "Salad", 8.00),
(10, "Green Bean Salad", "Salad", 8.00),
(11, "Tomato Salad", "Salad", 9.50),
(12, "Fennel Salad", "Salad", 8.50),
(13, "Fruit Salad", "Desert", 7.00),
(14, "Roasted Grapes", "Desert", 6.50),
(15, "Grilled Mango", "Desert", 8.50),
(16, "Grilled Waterlemon", "Desert", 8.50);
delete from Addresses;
insert into Addresses (AddressID, Street, PostalCode, City, Country) values
(1, 'Hauptstraße 1', '10115', 'Berlin', 'Germany'),
(2, 'Friedrichstraße 56', '10119', 'Berlin', 'Germany'),
(3, 'Kurfürstenstraße 135', '10115', 'Berlin', 'Germany'),
(4, 'Altenhofen 168', '10623', 'Berlin', 'Germany'),
(5, 'Hauptstraße 180', '10116', 'Berlin', 'Germany');
delete from Employees;
insert into Employees (
EmployeeID, FirstName, LastName, PhoneNumber, EmailAddress, Role, AnnualSalary, AddressID
) values
(1, 'Mario', 'Gollini', '+4915123456789', 'mario.gollini@littlelemon.de', 'Manager', 72383.00, 1),
(2, 'Adrian', 'Gollini', '+4916198765432', 'adrian.gollini@littlelemon.de', 'Receptionist', 74083.00, 2),
(3, 'Giorgos', 'Dioudis', '+4917654321098', 'giogos.dioudis@littlelemon.de', 'Assistant Chef', 77912.00, 3),
(4, 'Fatma', 'Kaya', '+4918765432190', 'fatma.kaya@littlelemon.de', 'Head Chef', 79314.00, 4),
(5, 'Elena', 'Salvai', '+4919876543210', 'elena.salvai@littlelemon.de', 'Waiter', 78921.00, 5);
delete from Customers;
insert into Customers (
CustomerID, FirstName, LastName, PhoneNumber, EmailAddress, AddressID
)
values
(1, 'Ahmed', 'Mohamed', '+20212345678', 'ahmed.mohamed@example.com', null),
(2, 'Sophie', 'Dupont', '+33612345678', 'sophie.dupont@example.com', null),
(3, 'Li Wei', 'Zhang', '+86123456789', 'li.wei.zhang@example.com', null),
(4, 'Maria', 'Rodriguez', '+34612345678', 'maria.rodriguez@example.com', null),
(5, 'Ivanov', 'Petrov', '+70987654321', 'ivanov.petrov@example.com', null),
(6, 'José', 'Martinez', '+54987654321', 'jose.martinez@example.com', null),
(7, 'Maria', 'Schmidt', '+49987654321', 'maria.schmidt@example.com', null),
(8, 'Thomas', 'Müller', '+44987654321', 'thomas.mueller@example.com', null),
(9, 'Ramón', 'García', '+58987654321', 'ramon.garcia@example.com', null),
(10, 'Lee', 'Kim', '+82109876543', 'lee.kim@example.com', null);
delete from Bookings;
insert into Bookings (BookingID, Reserved, TableNumber, CustomerID, EmployeeID)
values
(1, '2024-11-14 18:00:00', 1, 3, 1),
(2, '2024-11-14 19:30:00', 2, 2, 1),
(3, '2024-11-14 18:45:00', 3, 4, 1),
(4, '2024-11-14 19:15:00', 4, 1, 2),
(5, '2024-11-14 18:30:00', 5, 6, 2),
(6, '2024-11-14 19:00:00', 6, 5, 2),
(7, '2024-11-15 18:15:00', 1, 7, 5),
(8, '2024-11-15 19:45:00', 2, 8, 5),
(9, '2024-11-15 18:30:00', 3, 9, 2),
(10, '2024-11-15 19:00:00', 4, 10, 5);
insert into Orders (OrderID, Started, Finished, BillAmount, BookingID)
values
(1, '2024-11-14 18:05:00', '2023-10-01 19:25:00', null, 1),
(2, '2024-11-14 19:35:00', '2023-10-02 20:45:00', null, 2),
(3, '2024-11-14 19:10:00', '2023-10-03 20:30:00', null, 3),
(4, '2024-11-14 19:35:00', '2023-10-04 20:50:00', null, 4),
(5, '2024-11-14 19:40:00', '2023-10-05 20:55:00', null, 5),
(6, '2024-11-14 19:40:00', '2023-10-06 20:45:00', null, 6);
insert into OrderItems (OrderItemID, MenuItemID, OrderID, Ordered, Delivered, DeliveryStatus)
values
(1, 1, 1, '2024-11-14 18:05:00', '2024-11-14 18:25:00', 'Delivered'),
(2, 2, 1, '2024-11-14 18:05:00', '2024-11-14 18:25:00', 'Delivered'),
(3, 3, 1, '2024-11-14 18:05:00', '2024-11-14 18:25:00', 'Delivered'),
(4, 5, 1, '2024-11-14 18:05:00', '2024-11-14 18:40:00', 'Delivered'),
(5, 5, 1, '2024-11-14 18:05:00', '2024-11-14 18:40:00', 'Delivered'),
(6, 8, 1, '2024-11-14 18:05:00', '2024-11-14 18:40:00', 'Delivered'),
(7, 14, 1, '2024-11-14 18:05:00', '2024-11-14 19:05:00', 'Delivered'),
(8, 15, 1, '2024-11-14 18:05:00', '2024-11-14 19:05:00', 'Delivered'),
(9, 16, 1, '2024-11-14 18:05:00', '2024-11-14 19:05:00', 'Delivered'),
(10, 2, 2, '2024-11-14 19:35:00', '2024-11-14 19:55:00', 'Delivered'),
(11, 5, 2, '2024-11-14 19:35:00', '2024-11-14 20:10:00', 'Delivered'),
(12, 13, 2, '2024-11-14 19:35:00', '2024-11-14 20:35:00', 'Delivered'),
(13, 4, 3, '2024-11-14 19:10:00', '2024-11-14 19:30:00', 'Delivered'),
(14, 7, 3, '2024-11-14 19:10:00', '2024-11-14 19:45:00', 'Delivered'),
(15, 13, 3, '2024-11-14 19:10:00', '2024-11-14 20:10:00', 'Delivered'),
(16, 3, 4, '2024-11-14 19:35:00', '2024-11-14 19:55:00', 'Delivered'),
(17, 6, 4, '2024-11-14 19:35:00', '2024-11-14 20:10:00', 'Delivered'),
(18, 13, 4, '2024-11-14 19:35:00', '2024-11-14 20:35:00', 'Delivered'),
(19, 9, 5, '2024-11-14 19:40:00', '2024-11-14 20:00:00', 'Delivered'),
(20, 8, 5, '2024-11-14 19:40:00', '2024-11-14 20:15:00', 'Delivered'),
(22, 2, 6, '2024-11-14 19:40:00', '2024-11-14 19:55:00', 'Delivered'),
(23, 4, 6, '2024-11-14 19:40:00', '2024-11-14 19:55:00', 'Delivered'),
(24, 2, 6, '2024-11-14 19:40:00', '2024-11-14 19:55:00', 'Delivered'),
(25, 3, 6, '2024-11-14 19:40:00', '2024-11-14 19:55:00', 'Delivered'),
(26, 1, 6, '2024-11-14 19:40:00', '2024-11-14 19:55:00', 'Delivered'),
(27, 6, 6, '2024-11-14 19:40:00', '2024-11-14 20:10:00', 'Delivered'),
(28, 6, 6, '2024-11-14 19:40:00', '2024-11-14 20:10:00', 'Delivered'),
(29, 5, 6, '2024-11-14 19:40:00', '2024-11-14 20:10:00', 'Delivered'),
(30, 5, 6, '2024-11-14 19:40:00', '2024-11-14 20:10:00', 'Delivered'),
(31, 7, 6, '2024-11-14 19:40:00', '2024-11-14 20:10:00', 'Delivered'),
(32, 15, 6, '2024-11-14 19:40:00', '2024-11-14 20:35:00', 'Delivered'),
(33, 14, 6, '2024-11-14 19:40:00', '2024-11-14 20:35:00', 'Delivered'),
(34, 15, 6, '2024-11-14 19:40:00', '2024-11-14 20:35:00', 'Delivered'),
(35, 16, 6, '2024-11-14 19:40:00', '2024-11-14 20:35:00', 'Delivered'),
(36, 16, 6, '2024-11-14 19:40:00', '2024-11-14 20:35:00', 'Delivered');
update Orders as O
inner join
(
select
OI.OrderID,
sum(MI.Price) as TotalItemPrice
from OrderItems as OI
inner join MenuItems as MI on OI.MenuItemID = MI.MenuItemID
group by OI.OrderID
) as A
on O.OrderID = A.OrderID
set O.BillAmount = A.TotalItemPrice;

View File

@@ -0,0 +1,101 @@
# Task 1
> delimiter $
> source ./add_booking.sql
> delimiter ;
> call AddBooking(20, "2024-11-19 19:00:00", 3, 1, 2)
+-------------------+
| Confirmation |
+-------------------+
| New booking added |
+-------------------+
> select * from `Bookings` where `BookingID` = 20
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 20 | 2024-11-19 19:00:00 | 3 | 1 | 2 |
+-----------+---------------------+-------------+------------+------------+
# Task 2
> delimiter $
> source ./update_booking.sql
> delimiter ;
> call UpdateBooking(20, date_sub((select Reserved from `Bookings` where `BookingID` = 20), interval 1 day))
+--------------------+
| Confirmation |
+--------------------+
| Booking 20 updated |
+--------------------+
> select * from `Bookings` where `BookingID` = 20;
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 20 | 2024-11-18 19:00:00 | 3 | 1 | 2 |
+-----------+---------------------+-------------+------------+------------+
> call UpdateBooking(21, "2024-11-18 15:00:00")
+---------------------------------------------+
| Confirmation |
+---------------------------------------------+
| Booking 21 wasn't updated - missing booking |
+---------------------------------------------+
> select * from `Bookings` where `BookingID` = 21
+-----------+----------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+----------+-------------+------------+------------+
+-----------+----------+-------------+------------+------------+
# Task 3
> delimiter $
> source ./cancel_booking.sql
> delimiter ;
> call CancelBooking(20)
+----------------------+
| Confirmation |
+----------------------+
| Booking 20 cancelled |
+----------------------+
> select * from `Bookings`
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 1 | 2024-11-14 18:00:00 | 1 | 3 | 1 |
| 2 | 2024-11-14 19:30:00 | 2 | 2 | 1 |
| 3 | 2024-11-14 18:45:00 | 3 | 4 | 1 |
| 4 | 2024-11-14 19:15:00 | 4 | 1 | 2 |
| 5 | 2024-11-14 18:30:00 | 5 | 6 | 2 |
| 6 | 2024-11-14 19:00:00 | 6 | 5 | 2 |
| 7 | 2024-11-15 18:15:00 | 1 | 7 | 5 |
| 8 | 2024-11-15 19:45:00 | 2 | 8 | 5 |
| 9 | 2024-11-15 18:30:00 | 3 | 9 | 2 |
| 10 | 2024-11-15 19:00:00 | 4 | 10 | 5 |
| 11 | 2022-10-14 18:00:00 | 5 | 1 | 1 |
| 12 | 2022-11-12 19:30:00 | 3 | 3 | 1 |
| 13 | 2022-10-11 18:45:00 | 2 | 2 | 1 |
| 14 | 2022-10-13 19:15:00 | 2 | 1 | 1 |
| 15 | 2022-11-12 17:15:00 | 3 | 4 | 1 |
+-----------+---------------------+-------------+------------+------------+
> call CancelBooking(21)
+--------------------------------------------+
| Confirmation |
+--------------------------------------------+
| Booking 21 not cancelled - missing booking |
+--------------------------------------------+
> select * from `Bookings`
+-----------+---------------------+-------------+------------+------------+
| BookingID | Reserved | TableNumber | CustomerID | EmployeeID |
+-----------+---------------------+-------------+------------+------------+
| 1 | 2024-11-14 18:00:00 | 1 | 3 | 1 |
| 2 | 2024-11-14 19:30:00 | 2 | 2 | 1 |
| 3 | 2024-11-14 18:45:00 | 3 | 4 | 1 |
| 4 | 2024-11-14 19:15:00 | 4 | 1 | 2 |
| 5 | 2024-11-14 18:30:00 | 5 | 6 | 2 |
| 6 | 2024-11-14 19:00:00 | 6 | 5 | 2 |
| 7 | 2024-11-15 18:15:00 | 1 | 7 | 5 |
| 8 | 2024-11-15 19:45:00 | 2 | 8 | 5 |
| 9 | 2024-11-15 18:30:00 | 3 | 9 | 2 |
| 10 | 2024-11-15 19:00:00 | 4 | 10 | 5 |
| 11 | 2022-10-14 18:00:00 | 5 | 1 | 1 |
| 12 | 2022-11-12 19:30:00 | 3 | 3 | 1 |
| 13 | 2022-10-11 18:45:00 | 2 | 2 | 1 |
| 14 | 2022-10-13 19:15:00 | 2 | 1 | 1 |
| 15 | 2022-11-12 17:15:00 | 3 | 4 | 1 |
+-----------+---------------------+-------------+------------+------------+

View File

@@ -0,0 +1,11 @@
select
C.FirstName,
C.LastName,
C.PhoneNumber,
C.EmailAddress,
max(O.BillAmount) as MaxBillAmount
from Orders as O
inner join Bookings as B on O.BookingID = B.BookingID
inner join Customers as C on B.CustomerID = C.CustomerID
group by C.CustomerID
having MaxBillAmount > 60.00;

View File

@@ -0,0 +1,47 @@
# Task 1
> delimiter $
> source ./get_max_quantity.sql
> delimiter ;
> call GetMaxQuantity();
+--------------------+
| MaxQuantityInOrder |
+--------------------+
| 15 |
+--------------------+
# Task 2
> source ./get_order_detail.sql
> set @customerID = 1
> execute GetOrderDetail using @customerID
+---------+----------+-------+
| OrderID | Quantity | Cost |
+---------+----------+-------+
| 4 | 3 | 29.00 |
+---------+----------+-------+
# Task 3
> delimiter $
> source ./cancel_order.sql
> delimiter ;
> call CancelOrder(2)
+----------------------+
| Confirmation |
+----------------------+
| Order 2 is cancelled |
+----------------------+
> select * from `Orders`
+---------+---------------------+---------------------+------------+-----------+
| OrderID | Started | Finished | BillAmount | BookingID |
+---------+---------------------+---------------------+------------+-----------+
| 1 | 2024-11-14 18:05:00 | 2023-10-01 19:25:00 | 82.50 | 1 |
| 3 | 2024-11-14 19:10:00 | 2023-10-03 20:30:00 | 31.00 | 3 |
| 4 | 2024-11-14 19:35:00 | 2023-10-04 20:50:00 | 29.00 | 4 |
| 5 | 2024-11-14 19:40:00 | 2023-10-05 20:55:00 | 20.00 | 5 |
| 6 | 2024-11-14 19:40:00 | 2023-10-06 20:45:00 | 150.50 | 6 |
+---------+---------------------+---------------------+------------+-----------+
> call CancelOrder(2)
+-----------------------+
| Confirmation |
+-----------------------+
| Order 2 doesn't exist |
+-----------------------+

View File

@@ -0,0 +1,40 @@
# Task 1
> source ./create_orders_view.sql
> select * from OrdersView;
+---------+----------+--------+
| OrderID | Quantity | Cost |
+---------+----------+--------+
| 1 | 9 | 82.50 |
| 2 | 3 | 28.00 |
| 3 | 3 | 31.00 |
| 4 | 3 | 29.00 |
| 6 | 15 | 150.50 |
+---------+----------+--------+
# Task 2
> source ./get_expensive_orders.sql
+------------+---------------+---------+--------+-----------------------+--------------------------+
| CustomerID | FullName | OrderID | Cost | Mains | Starters |
+------------+---------------+---------+--------+-----------------------+--------------------------+
| 3 | Li Wei Zhang | 1 | 82.50 | Gigantes Plaki (2x) | Grilled Artichokes (1x) |
| | | | | Pasta alla Norma (1x) | Lentil Soup (1x) |
| | | | | | Humus Plate (1x) |
| 5 | Ivanov Petrov | 6 | 150.50 | Minestrone (2x) | Lentil Soup (2x) |
| | | | | Gigantes Plaki (2x) | Eggplant Bruschetta (1x) |
| | | | | Moussaka (1x) | Humus Plate (1x) |
| | | | | | Grilled Artichokes (1x) |
+------------+---------------+---------+--------+-----------------------+--------------------------+
# Task 3
> source ./get_popular_menu_items.sql
+--------------------+---------------+
| Name | OrderQuantity |
+--------------------+---------------+
| Lentil Soup | 4 |
| Humus Plate | 3 |
| Gigantes Plaki | 5 |
| Minestrone | 3 |
| Fruit Salad | 3 |
| Grilled Mango | 3 |
| Grilled Waterlemon | 3 |
+--------------------+---------------+

17
sql/table_is_free.sql Normal file
View File

@@ -0,0 +1,17 @@
-- available bookings - task 2
-- changes:
-- changed BookingDate (Reserved) data type from date to datetime, because I assumed that Little Lemon
-- allows wants bookings per day and table - a table is considered blocked for 2 hours
-- noqa: disable=CP03,LT02,RF03
create function TableIsFree(Reserved datetime, TableNumber int)
returns boolean
deterministic
begin
return (select not exists (
select BookingID
from Bookings as B
where
abs(timestampdiff(second, B.Reserved, Reserved)) < 2 * 60 * 60
and B.TableNumber = TableNumber
));
end

16
sql/update_booking.sql Normal file
View File

@@ -0,0 +1,16 @@
-- modify bookings - task 2
-- changes:
-- changed BookingDate (Reserved) data type from date to datetime, because I assumed that Little Lemon
-- allows wants bookings per day and table - a table is considered blocked for 2 hours
-- noqa: disable=CP03,LT02,RF03
create procedure UpdateBooking (in BookingID int, in Reserved datetime)
begin
update Bookings as B
set B.Reserved = Reserved
where B.BookingID = BookingID;
if row_count() = 1 then
select concat("Booking ", BookingID, " updated") as Confirmation;
else
select concat("Booking ", BookingID, " wasn't updated - missing booking") as Confirmation;
end if;
end