MySQL - Daily overview only shows current bookings rather than all - mysql

I'm trying to get a view where I can select a specific day and it will return a list of ALL rooms with the current days breakdown.
Currently it shows the daily overview for a specific day but lacks showing rooms which DONT have bookings for the day selected?
(I need to display the daily overview for all rooms even without bookings)
How do I change this so it will show ALL rooms even when there is not a booking for the specific room on that day as well as rooms that do?
Current display (for 2019-11-24):
SELECT * FROM `Daily Overview` WHERE '2019-11-24' >= StartDate AND '2019-11-24' <= EndDate;
3 Family 2 2019-11-24 2019-11-28 4 2 2,3 Ahri,Blitz
4 Dual 1 2019-11-22 2019-11-25 2 1 1 Aatrox
Desired output:
1
2
3 Family 2 2019-11-24 2019-11-28 4 2 2,3 Ahri,Blitz
4 Dual 1 2019-11-22 2019-11-25 2 1 1 Aatrox
5
6
8
9
10
View script:
CREATE VIEW `Daily Overview` AS
SELECT ROOMINFO.ID as Room, ROOMINFO.`Type`,
BOOKROOM.Ref as Ref,
BOOK.Start_Date as StartDate,
BOOK.End_Date as EndDate
FROM ROOMINFO
JOIN BOOKROOM ON ROOMINFO.ID = BOOKROOM.ID
JOIN BOOK ON ROOMINFO.ID AND BOOK.Ref = BOOKROOM.Ref
GROUP BY ROOMINFO.ID, ROOMINFO.`Type`, ROOMINFO.Max, Ref;
table data & structure script:
CREATE SCHEMA CATTERY2;
USE CATTERY2;
CREATE TABLE BOOK( Ref INT NOT NULL AUTO_INCREMENT, Start_Date DATE NOT NULL, End_Date DATE NOT NULL, PRIMARY KEY(Ref));
CREATE TABLE ROOMINFO( ID INT NOT NULL AUTO_INCREMENT,`Type` VARCHAR(10) NOT NULL, Max TINYINT NOT NULL, PRIMARY KEY(ID));
CREATE TABLE BOOKROOM( Ref INT NOT NULL, ID INT NOT NULL, FOREIGN KEY (Ref) REFERENCES BOOK(Ref), FOREIGN KEY (ID) REFERENCES ROOMINFO(ID));
INSERT INTO BOOK(Start_Date, End_Date) VALUES
("2019-11-22", "2019-11-25"), ("2019-11-24", "2019-11-28");
INSERT INTO ROOMINFO (ID, `Type`,Max) VALUES
(1, "Family", 4), (2, "Family", 4), (3, "Family", 4), (4, "Dual", 2),
(5, "Dual", 2), (6, "Dual", 2), (7, "Dual", 2), (8, "Dual", 2),
(9, "Dual", 2), (10, "Dual", 2);
INSERT INTO BOOKROOM( Ref, ID ) VALUES
(1, 4), (2, 3);

Consider the following. Note that I've modified your structure slightly to aid comprehension:
DROP SCHEMA IF EXISTS cattery2;
CREATE SCHEMA CATTERY2;
USE CATTERY2;
CREATE TABLE cats
(cat_id SERIAL PRIMARY KEY
,name VARCHAR(30)
,sex CHAR(1) NOT NULL
);
CREATE TABLE bookings
(booking_id SERIAL PRIMARY KEY
,start_Date DATE NOT NULL
,end_date DATE NOT NULL
);
CREATE TABLE rooms
(room_id SERIAL PRIMARY KEY
,capacity TINYINT NOT NULL
);
CREATE TABLE room_booking
(room_id INT NOT NULL
,booking_id INT NOT NULL
,PRIMARY KEY(room_id,booking_id)
);
CREATE TABLE cat_booking
(cat_id INT NOT NULL
,booking_id INT NOT NULL
,PRIMARY KEY(cat_id,booking_id)
);
INSERT INTO bookings(start_date, end_date) VALUES
("2019-11-22", "2019-11-25"),
("2019-11-24", "2019-11-28"),
("2019-12-01", "2019-12-02"),
("2019-12-01", "2019-12-06"),
("2019-12-02", "2019-12-03"),
("2019-12-04", "2019-12-10"),
("2019-12-04", "2019-12-10"),
("2019-12-05", "2019-12-13"),
("2019-12-16", "2019-12-19"),
("2019-12-26", "2019-12-28"),
("2019-12-26", "2020-01-01"),
("2019-12-28", "2020-01-02"),
("2019-12-31", "2020-01-05"),
("2020-01-03", "2020-01-08"),
("2020-01-05", "2020-01-11"),
("2020-01-06", "2020-01-09"),
("2020-01-06", "2020-01-11"),
("2020-01-08", "2020-01-18"),
("2020-01-11", "2020-01-15"),
("2020-01-15", "2020-01-17"),
("2020-01-15", "2020-01-18");
INSERT INTO rooms (room_id,capacity) VALUES
( 1,4),
( 2,4),
( 3,4),
( 4,2),
( 5,2),
( 6,2),
( 7,2),
( 8,2),
( 9,2),
(10,2);
INSERT INTO room_booking (booking_id,room_id) VALUES
( 1, 4),
( 2, 3),
( 3, 4),
( 4, 5),
( 5, 6),
( 6, 7),
( 7, 3),
( 8, 2),
( 9, 1),
(10, 8),
(11, 3),
(12, 9),
(13, 2),
(14, 10),
(15, 4),
(16, 5),
(17, 6),
(18, 7),
(19, 2),
(20, 1),
(21, 10);
INSERT INTO cats (name,sex) VALUES
('Aatrox', 'm'),
('Ahri', 'f'),
('Blitz', 'm'),
('Curley', 'm'),
('Mandy', 'm'),
('Nami', 'f'),
('Kog', 'm'),
('Caitlyn', 'f'),
('Barney', 'm'),
('Gnar', 'm'),
('Charley', 'f'),
('Bundy', 'm'),
('Ringo', 'm'),
('Smiley', 'm'),
('Bentley', 'f'),
('Barney', 'm'),
('Yuumi', 'f'),
('Rammus', 'm'),
('Viktor', 'm'),
('Xerath', 'm'),
('Azir', 'm'),
('Reginald', 'm'),
('Harry', 'm'),
('Indie', 'f'),
('Dotty', 'f'),
('Wesley', 'm'),
('Karma', 'f'),
('Nami', 'f'),
('Nautalus', 'm'),
('Tristy', 'f'),
('Kaisa', 'f'),
('Baron', 'm'),
('Braum', 'm'),
('Alistar', 'm'),
('Ahri', 'f');
INSERT INTO cat_booking(booking_id,cat_id) VALUES
( 1, 1),
( 2, 2),
( 2, 3),
( 3, 1),
( 4, 2),
( 5, 3),
( 5, 4),
( 6, 5),
( 7, 6),
( 7, 7),
( 7, 8),
( 8, 9),
( 8, 10),
( 8, 11),
( 9, 12),
( 9, 13),
( 9, 14),
(10, 15),
(11, 16),
(11, 17),
(11, 18),
(12, 19),
(12, 20),
(13, 21),
(13, 22),
(13, 23),
(14, 24),
(14, 25),
(15, 26),
(16, 27),
(16, 28),
(17, 29),
(18, 30),
(19, 31),
(19, 32),
(20, 33),
(20, 34),
(21, 35);
And the query...
SELECT DISTINCT r.room_id
, r.capacity
, x.booking_id
, x.start_date
, x.end_date
FROM rooms r
LEFT
JOIN
( SELECT b.booking_id
, b.start_date
, b.end_date
, rb.room_id
FROM bookings b
JOIN room_booking rb
ON rb.booking_id = b.booking_id
WHERE '2019-11-24' BETWEEN b.start_date AND b.end_date
) x
ON x.room_id = r.room_id
ORDER
BY r.room_id
, r.capacity
, x.start_date
, x.booking_id;
+---------+----------+------------+------------+------------+
| room_id | capacity | booking_id | start_date | end_date |
+---------+----------+------------+------------+------------+
| 1 | 4 | NULL | NULL | NULL |
| 2 | 4 | NULL | NULL | NULL |
| 3 | 4 | 2 | 2019-11-24 | 2019-11-28 |
| 4 | 2 | 1 | 2019-11-22 | 2019-11-25 |
| 5 | 2 | NULL | NULL | NULL |
| 6 | 2 | NULL | NULL | NULL |
| 7 | 2 | NULL | NULL | NULL |
| 8 | 2 | NULL | NULL | NULL |
| 9 | 2 | NULL | NULL | NULL |
| 10 | 2 | NULL | NULL | NULL |
+---------+----------+------------+------------+------------+
Also, note that there's no VIEW here. This is deliberate. Queries against VIEWS in MySQL have limited access to underlying indexes, making them (in my VIEW) almost useless.

Related

MySQL Query to get profit and loss on asset by order history

Hello everyone I am getting a problem with the MySQL query and had confusion. I want to achieve profit and loss on assets from order history.
IGNORE coin_current_price in output not needed.
resultBuy = TotalBuyingPrice / totalCoinQtyBuy
if sellingPrice_Per_Coin is greater than resultBuy then it will be profitable and vice-versa for loss.
Coin Name, coin_currency are the same then it will be called as a single entity. if any of these conditions don't meet then it will create a new row for that item like usdt and INR
admin_id will be the user id only of that user data will show.
MySQL Query - For Two Table buy_table and sell_table
CREATE TABLE buy_table (
id INTEGER PRIMARY KEY,
coin_name TEXT NOT NULL,
coin_qty TEXT NOT NULL,
coin_buy_price TEXT NOT NULL,
coin_current_price TEXT NOT NULL,
admin_id TEXT NOT NULL,
priority TEXT NOT NULL,
coin_currency TEXT NOT NULL
);
CREATE TABLE sell_table (
id INTEGER PRIMARY KEY,
coin_name TEXT NOT NULL,
coin_qty TEXT NOT NULL,
coin_buy_price TEXT NOT NULL,
coin_current_price TEXT NOT NULL,
admin_id TEXT NOT NULL,
priority TEXT NOT NULL,
coin_currency TEXT NOT NULL
);
INSERT INTO buy_table VALUES (1, 'BTC', '1', '$10', '$10', '11', '1','inr');
INSERT INTO buy_table VALUES (2, 'ETH', '1', '$10', '$10', '11', '1', 'inr');
INSERT INTO buy_table VALUES (3, 'BTC', '1', '$10', '$10', '11', '1', 'inr');
INSERT INTO buy_table VALUES (4, 'ETH', '4', '$10', '$10', '11', '1', 'inr');
INSERT INTO buy_table VALUES (5, 'BTC', '3', '$10', '$10', '11', '1', 'inr');
INSERT INTO buy_table VALUES (6, 'WRX', '1', '$1', '$1', '11', '1', 'inr');
INSERT INTO buy_table VALUES (7, 'WRX', '1', '$1', '$1', '11', '1', 'usdt');
INSERT INTO buy_table VALUES (8, 'WRX', '1', '$1', '$1', '11', '1', 'inr');
INSERT INTO buy_table VALUES (9, 'WRX', '1', '$1', '$1', '11', '1', 'usdt');
INSERT INTO sell_table VALUES (1, 'ETH', '1', '$50', '$50', '11' , '1', 'inr');
INSERT INTO sell_table VALUES (2, 'BTC', '1', '$50', '$50', '11' , '1', 'inr');
Final Output Will Be Like Below
| Coin Name | coin_currency | Buying Price | CoinQty | PricePerCoin | SellingPrice | SoldQty | ProfitLoss | Stock | admin_id |
| --------- | ------------- | ------------ | ------- | ------------ | ------------ | ------- | ---------- | ----- | -------- |
| BTC | INR | $50 | 5 | $10 | $50 | 1 | $40 | 4 | 11 |
| ETH | INR | $50 | 5 | $10 | $50 | 1 | $40 | 4 | 11 |
| WRX | INR | $2 | 2 | $1 | 0 | 0 | 0 | 1 | 11 |
| WRX | USDT | $2 | 2 | $1 | 0 | 0 | 0 | 1 | 11 |
This is the table structure if required with an online query run. Link for table
I would combine the two tables into one transactions table but for this answer I will leave the tables split as you have them.
Firstly, let's address the issues with the datatypes used in your two tables. TEXT is not the appropriate datatype for any of your columns. I have changed them to what I believe to be more appropriate types.
CREATE TABLE buy_table (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
coin_name VARCHAR(6) NOT NULL,
coin_qty SMALLINT UNSIGNED NOT NULL,
coin_buy_price DECIMAL(12, 2) NOT NULL,
coin_current_price DECIMAL(12, 2) NOT NULL,
admin_id INTEGER UNSIGNED NOT NULL,
priority TINYINT UNSIGNED NOT NULL,
coin_currency CHAR(3) NOT NULL
);
/* I am omitting the amended sell_table as it is identical in all but name */
INSERT INTO buy_table VALUES
(1, 'BTC', 1, 10, 10, 11, 1, 'inr'),
(2, 'ETH', 1, 10, 10, 11, 1, 'inr'),
(3, 'BTC', 1, 10, 10, 11, 1, 'inr'),
(4, 'ETH', 4, 10, 10, 11, 1, 'inr'),
(5, 'BTC', 3, 10, 10, 11, 1, 'inr'),
(6, 'WRX', 1, 1, 1, 11, 1, 'inr'),
(7, 'WRX', 1, 1, 1, 11, 1, 'usd'),
(8, 'WRX', 1, 1, 1, 11, 1, 'inr'),
(9, 'WRX', 1, 1, 1, 11, 1, 'usd');
INSERT INTO sell_table VALUES
(1, 'ETH', 1, 50, 50, 11, 1, 'inr'),
(2, 'BTC', 1, 50, 50, 11, 1, 'inr');
For this query I have chosen to aggregate the data on each side of the union in the derived table. This could be done in just the outer select list but it made some of the calculations more readable. As you suggested this will be run with the context of an admin_id I have included this as a predicate on the inner queries and not included the admin_id in the outer select list -
SELECT
`coin_name`,
UPPER(`coin_currency`) AS `coin_currency`,
SUM(`total_paid`) AS `buying_price`,
SUM(`num_bought`) AS `coin_qty`,
SUM(`avg_paid`) AS `price_per_coin`,
SUM(`total_received`) AS `selling_price`,
SUM(`num_sold`) AS `sold_qty`,
ROUND((SUM(`avg_received`) - SUM(`avg_paid`)) * SUM(`num_sold`), 2) AS `profit_loss`,
SUM(`num_bought`) - SUM(`num_sold`) AS `stock`
FROM (
SELECT
`coin_name`,
`coin_currency`,
SUM(`coin_qty`) AS `num_bought`,
0 AS `num_sold`,
SUM(`coin_qty` * `coin_buy_price`) AS `total_paid`,
0 AS `total_received`,
ROUND(SUM(`coin_qty` * `coin_buy_price`) / SUM(`coin_qty`), 2) AS `avg_paid`,
0 AS `avg_received`
FROM buy_table
WHERE `admin_id` = 11
GROUP BY `coin_name`, `coin_currency`
UNION ALL
SELECT
`coin_name`,
`coin_currency`,
0 AS `num_bought`,
SUM(`coin_qty`) AS `num_sold`,
0 AS `total_paid`,
SUM(`coin_qty` * `coin_buy_price`) AS `total_received`,
0 AS `avg_paid`,
ROUND(SUM(`coin_qty` * `coin_buy_price`) / SUM(`coin_qty`), 2) AS `avg_received`
FROM sell_table
WHERE `admin_id` = 11
GROUP BY `coin_name`, `coin_currency`
) tx
GROUP BY `coin_name`, `coin_currency`;
This would be cleaner if all the transactions were in a single table.
UPDATE - tables combined into single transactions table
CREATE TABLE transactions (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
type ENUM('buy', 'sell') NOT NULL,
coin_name VARCHAR(6) NOT NULL,
coin_qty SMALLINT UNSIGNED NOT NULL,
coin_buy_price DECIMAL(12, 2) NOT NULL,
coin_current_price DECIMAL(12, 2) NOT NULL,
admin_id INTEGER UNSIGNED NOT NULL,
priority TINYINT UNSIGNED NOT NULL,
coin_currency CHAR(3) NOT NULL
);
INSERT INTO transactions VALUES
( 1, 'buy', 'BTC', 1, 10, 10, 11, 1, 'inr'),
( 2, 'buy', 'ETH', 1, 10, 10, 11, 1, 'inr'),
( 3, 'buy', 'BTC', 1, 10, 10, 11, 1, 'inr'),
( 4, 'buy', 'ETH', 4, 10, 10, 11, 1, 'inr'),
( 5, 'buy', 'BTC', 3, 10, 10, 11, 1, 'inr'),
( 6, 'buy', 'WRX', 1, 1, 1, 11, 1, 'inr'),
( 7, 'buy', 'WRX', 1, 1, 1, 11, 1, 'usd'),
( 8, 'buy', 'WRX', 1, 1, 1, 11, 1, 'inr'),
( 9, 'buy', 'WRX', 1, 1, 1, 11, 1, 'usd'),
(10, 'sell', 'ETH', 1, 50, 50, 11, 1, 'inr'),
(11, 'sell', 'BTC', 1, 50, 50, 11, 1, 'inr');
SELECT
`coin_name`,
`coin_currency`,
`total_paid` AS `buying_price`,
`bought_qty` AS `coin_qty`,
ROUND(`total_paid` / `bought_qty`, 2) AS `price_per_coin`,
`total_received` AS `selling_price`,
`sold_qty`,
ROUND(IFNULL(((`total_received` / `sold_qty`) - (`total_paid` / `bought_qty`)) * `sold_qty`, 0), 2) AS `profit_loss`,
`bought_qty` - `sold_qty` AS `stock`
FROM (
SELECT
`coin_name`,
UPPER(`coin_currency`) AS `coin_currency`,
SUM(IF(`type` = 'buy', `coin_qty`, 0)) AS `bought_qty`,
SUM(IF(`type` = 'sell', `coin_qty`, 0)) AS `sold_qty`,
SUM(IF(`type` = 'buy', `coin_qty` * `coin_buy_price`, 0)) AS `total_paid`,
SUM(IF(`type` = 'sell', `coin_qty` * `coin_buy_price`, 0)) AS `total_received`
FROM transactions
WHERE `admin_id` = 11
GROUP BY `coin_name`, `coin_currency`
) tx;
db<>fiddle

How do I build a query to get the latest row per user where a third criteria is in a separate table?

I have three tables
CREATE TABLE `LineItems` (
`LineItemID` int NOT NULL,
`OrderID` int NOT NULL,
`ProductID` int NOT NULL
);
INSERT INTO `LineItems` (`LineItemID`, `OrderID`, `ProductID`) VALUES
(1, 1, 2),
(2, 1, 1),
(3, 2, 3),
(4, 2, 4),
(5, 3, 1),
(6, 4, 2),
(7, 5, 4),
(8, 5, 2),
(9, 5, 3),
(10, 6, 1),
(11, 6, 4),
(12, 7, 4),
(13, 7, 1),
(14, 7, 2),
(15, 8, 1),
(16, 9, 3),
(17, 9, 4),
(18, 10, 3);
CREATE TABLE `Orders` (
`OrderID` int NOT NULL,
`UserID` int NOT NULL,
`OrderDate` datetime NOT NULL
);
INSERT INTO `Orders` (`OrderID`, `UserID`, `OrderDate`) VALUES
(1, 21, '2021-05-01 00:00:00'),
(2, 21, '2021-05-03 00:00:00'),
(3, 24, '2021-05-06 00:00:00'),
(4, 23, '2021-05-12 00:00:00'),
(5, 21, '2021-05-14 00:00:00'),
(6, 22, '2021-05-16 00:00:00'),
(7, 23, '2021-05-20 00:00:00'),
(8, 21, '2021-05-22 00:00:00'),
(9, 24, '2021-05-23 00:00:00'),
(10, 23, '2021-05-26 00:00:00');
CREATE TABLE `Products` (
`ProductID` int NOT NULL,
`ProductTitle` VARCHAR(250) NOT NULL,
`ProductType` enum('doors','windows','flooring') NOT NULL
);
INSERT INTO `Products` (`ProductID`, `ProductTitle`, `ProductType`) VALUES
(1, 'French Doors','doors'),
(2, 'Sash Windows','windows'),
(3, 'Sliding Doors','doors'),
(4, 'Parquet Floor','flooring');
SQL Fiddle:
Orders - contains an order date and a user id
LineItems - Foreign key to the orders table, contains product ids that are in the order
Products - Contains details of the products (including if they are a door, window, or flooring)
I have figured out how to get the latest order per user with
SELECT O.* FROM Orders O LEFT JOIN Orders O2
ON O2.UserID=O.UserID AND O.OrderDate < O2.OrderDate
WHERE O2.OrderDate IS NULL;
This works fine and is included in the SQL fiddle, along with a query that returns a complete picture for reference.
I am trying to figure out how to get the latest order with flooring per user, but I'm not having any luck.
In the SQL fiddle linked above, the intended output for what I am after would be
OrderID | UserID | OrderDate
6 | 22 | 2021-05-16T00:00:00Z
5 | 21 | 2021-05-14T00:00:00Z
9 | 24 | 2021-05-23T00:00:00Z
7 | 23 | 2021-05-20T00:00:00Z
EDIT: To clarify, in the intended result, two rows (for users 21 and 23) are different than in the query that gets just latest order per user. This is because order IDs 8 and 10 (from the latest order per user query) do not include flooring. The intended query has to find the latest order with flooring from each user to return in the result set.
You need to add the LineItems and Products tables to your query to find orders where flooring was purchased:
SELECT DISTINCT O.*
FROM Orders O
LEFT JOIN Orders O2
ON O2.UserID=O.UserID AND
O.OrderDate < O2.OrderDate
INNER JOIN LineItems i
ON i.OrderID = O.OrderID
INNER JOIN Products p
ON p.ProductID = i.ProductID
WHERE O2.OrderDate IS NULL AND
p.ProductType = 'flooring'
db<>fiddle here

Get summary grouped by category with three tables on mysql

category
---------------------------
id_category primary key
category
id_user foreign key
counterpart
---------------------------
id_counterpart primary key
counterpart
id_category foreign key
id_user foreign key
transaction
---------------------------
transaction primary key
date
id_counterpart foreign key
amount
id_card foreign key
id_user foreign key
Hello,
I have thoses table on mysql database and i want to have summary of each category (with 0 if there is any transaction) by month and year based on id_user.
I tried this command to have grouped by counterpart and it works but cannot reach when i add category and group by id_category.
select counterpart, s2.total from counterpart as s1
left join (select coalesce(sum(amount),0) as total, id_counterpart from transaction where year(date) = 2019 and month(date) = 7 and id_user = 2 group by id_counterpart) as s2
on s1.id_counterpart = s2.id_counterpart
left join category on s1.id_category = category.id_category
group by counterpart;
Do you have any idea to do that ? Else, i will do with php.
Thank you.
Edit : Add example
INSERT INTO `category` (`id_category`, `category`, `id_user`) VALUES
(1, 'cat_a', 1),
(2, 'cat_b', 1),
(3, 'cat_c', 1);
INSERT INTO `counterpart` (`id_counterpart`, `counterpart`, `id_category`, `id_user`) VALUES
(1, 'cp_a', 1, 1),
(2, 'cp_b', 2, 1),
(3, 'cp_c', 2, 1);
INSERT INTO `transaction` (`id_transaction`, `date`, `id_counterpart`, `amount`, `id_card`, `id_user`) VALUES
(1, '2019-07-01 00:00:00', 1, 400.00, 2, 1),
(2, '2019-07-01 00:00:00', 1, -24.95, 2, 1),
(3, '2019-07-31 00:00:00', 2, -20.04, 2, 1);
(4, '2019-07-30 00:00:00', 2, -1.00, 2, 1);
(5, '2019-07-29 00:00:00', 3, -2.00, 2, 1);
(6, '2019-07-28 00:00:00', 1, -3.00, 2, 1);
(7, '2019-07-27 00:00:00', 3, 2.00, 2, 1);
(8, '2019-07-26 00:00:00', 2, 5.00, 2, 1);
On july 2019 i want to have this, for user 1 :
cat_a 372.05
cat_b 16.04
cat_c 0.00
Join the tables and then group by category:
select c.category, coalesce(sum(t.amount), 0) total
from category c
left join counterpart as cp
on c.id_category = cp.id_category and c.id_user = cp.id_user
left join transaction t
on t.id_counterpart = cp.id_counterpart and t.id_user = cp.id_user and year(t.date) = 2019 and month(t.date) = 7 and t.id_user = 1
group by c.id_category, c.category
See the demo.
Results:
| category | total |
| -------- | ------ |
| cat_a | 372.05 |
| cat_b | -16.04 |
| cat_c | 0 |

Select rows grouped by a column having max aggregate

Given the following data set, how would I find the email addresses that were references for the most ApplicationIDs that have an "Accepted" decision?
CREATE TABLE IF NOT EXISTS `EmailReferences` (
`ApplicationID` INT NOT NULL,
`Email` VARCHAR(45) NOT NULL,
PRIMARY KEY (`ApplicationID`, `Email`)
);
INSERT INTO EmailReferences (ApplicationID, Email)
VALUES
(1, 'ref10#test.org'), (1, 'ref11#test.org'), (1, 'ref12#test.org'),
(2, 'ref20#test.org'), (2, 'ref21#test.org'), (2, 'ref22#test.org'),
(3, 'ref11#test.org'), (3, 'ref31#test.org'), (3, 'ref32#test.org'),
(4, 'ref40#test.org'), (4, 'ref41#test.org'), (4, 'ref42#test.org'),
(5, 'ref50#test.org'), (5, 'ref51#test.org'), (5, 'ref52#test.org'),
(6, 'ref60#test.org'), (6, 'ref11#test.org'), (6, 'ref62#test.org'),
(7, 'ref70#test.org'), (7, 'ref71#test.org'), (7, 'ref72#test.org'),
(8, 'ref10#test.org'), (8, 'ref81#test.org'), (8, 'ref82#test.org')
;
CREATE TABLE IF NOT EXISTS `FinalDecision` (
`ApplicationID` INT NOT NULL,
`Decision` ENUM('Accepted', 'Denied') NOT NULL,
PRIMARY KEY (`ApplicationID`)
);
INSERT INTO FinalDecision (ApplicationID, Decision)
VALUES
(1, 'Accepted'), (2, 'Denied'),
(3, 'Accepted'), (4, 'Denied'),
(5, 'Denied'), (6, 'Denied'),
(7, 'Denied'), (8, 'Accepted')
;
Fiddle of same:http://sqlfiddle.com/#!9/03bcf2/1
Initially, I was using LIMIT 1 and ORDER BY CountDecision DESC, like so:
SELECT er.email, COUNT(fd.Decision) AS CountDecision
FROM EmailReferences AS er
JOIN FinalDecision AS fd ON er.ApplicationID = fd.ApplicationID
WHERE fd.Decision = 'Accepted'
GROUP BY er.email
ORDER BY CountDecision DESC
LIMIT 1
;
However, it occurred to me that I could have multiple email addresses that referred different "most accepted" decisions (i.e., a tie, so to speak), and those would be filtered out (is that the right phrasing?) with the LIMIT keyword.
I then tried a variation on the above query, replacing the ORDER BY and LIMIT lines with:
HAVING MAX(CountDecision)
But I realized that that's only half a statement: MAX(CountDecision) needs to be compared to something. I just don't know what.
Any pointers would be much appreciated. Thanks!
Note: this is for a homework assignment.
Update: To be clear, I'm trying to find value and count of Emails from EmailReferences. However, I only want rows that have FinalDecision.Decision = 'Accepted' (on matching ApplicantIDs). Based on my data, the result should be:
Email | CountDecision
---------------+--------------
ref10#test.org | 2
ref11#test.org | 2
For example...
SELECT a.*
FROM
( SELECT x.email
, COUNT(*) total
FROM emailreferences x
JOIN finaldecision y
ON y.applicationid = x.applicationid
WHERE y.decision = 'accepted'
GROUP
BY x.email
) a
JOIN
( SELECT COUNT(*) total
FROM emailreferences x
JOIN finaldecision y
ON y.applicationid = x.applicationid
WHERE y.decision = 'accepted'
GROUP
BY x.email
ORDER
BY total DESC
LIMIT 1
) b
ON b.total = a.total;
MySQL still lack window functions, but when version 8 is production ready, this becomes easier. So for fuure reference, or for those databases like Mariadb that already have window functions:
CREATE TABLE IF NOT EXISTS `EmailReferences` (
`ApplicationID` INT NOT NULL,
`Email` VARCHAR(45) NOT NULL,
PRIMARY KEY (`ApplicationID`, `Email`)
);
INSERT INTO EmailReferences (ApplicationID, Email)
VALUES
(1, 'ref10#test.org'), (1, 'ref11#test.org'), (1, 'ref12#test.org'),
(2, 'ref20#test.org'), (2, 'ref21#test.org'), (2, 'ref22#test.org'),
(3, 'ref30#test.org'), (3, 'ref31#test.org'), (3, 'ref32#test.org'),
(4, 'ref40#test.org'), (4, 'ref41#test.org'), (4, 'ref42#test.org'),
(5, 'ref50#test.org'), (5, 'ref51#test.org'), (5, 'ref52#test.org'),
(6, 'ref60#test.org'), (6, 'ref11#test.org'), (6, 'ref62#test.org'),
(7, 'ref70#test.org'), (7, 'ref71#test.org'), (7, 'ref72#test.org'),
(8, 'ref10#test.org'), (8, 'ref81#test.org'), (8, 'ref82#test.org')
;
CREATE TABLE IF NOT EXISTS `FinalDecision` (
`ApplicationID` INT NOT NULL,
`Decision` ENUM('Accepted', 'Denied') NOT NULL,
PRIMARY KEY (`ApplicationID`)
);
INSERT INTO FinalDecision (ApplicationID, Decision)
VALUES
(1, 'Accepted'), (2, 'Denied'),
(3, 'Accepted'), (4, 'Denied'),
(5, 'Denied'), (6, 'Denied'),
(7, 'Denied'), (8, 'Accepted')
;
select email, CountDecision
from (
SELECT er.email, COUNT(fd.Decision) AS CountDecision
, max(COUNT(fd.Decision)) over() maxCountDecision
FROM EmailReferences AS er
JOIN FinalDecision AS fd ON er.ApplicationID = fd.ApplicationID
WHERE fd.Decision = 'Accepted'
GROUP BY er.email
) d
where CountDecision = maxCountDecision
email | CountDecision
:------------- | ------------:
ref10#test.org | 2
dbfiddle here

Mysql query using group_concat and self referencing id in the table

Here is my table and sample data.
CREATE TABLE `sections` (
`section_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`parent_section_id` int(11) DEFAULT NULL,
PRIMARY KEY (`section_id`)
);
INSERT INTO `sections` (`section_id`, `name`, `parent_section_id`) VALUES
(1, 'City', NULL),
(2, 'Supplements', 4),
(3, 'News', 5),
(4, 'Sunday', 2),
(5, 'Monday', 2),
(6, 'Tuesday', 2),
(7, 'Wednesday', 2),
(8, 'Thursday', 2),
(9, 'Friday', 2),
(10, 'Saturday', 2),
(11, 'Home', 4),
(12, 'Games', 4),
(13, 'Sites', 5),
(14, 'Sports', 5),
(15, 'Cyber Space', 6);
parent_section_id is foreign key referencing to section_id in the same table which can have null if it doesn't belong to any other section.
How can I get the below output I have tried using group_concat function but it doesn't give the exact result. The parent_section_id is pointing to id from the same table. Should I use any other column to achieve the below output or use some other table to keep track of Sections which contains sub sections.
Please help me solve this problem or suggest any other approach
id, Name, SubSections
----------------------
1, 'City', null
2, 'Supplements', 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'
3, 'News', null
4, 'Sunday', 'Homes,Games'
5, 'Monday','Sites,Sports'
6, 'Tuesday', 'Cyber Space'
7, 'Wednesday', null
8, 'Thursday', null
9, 'Friday', null
10, 'Saturday', null
11, 'Home', null
12, 'Games', null
13, 'Site', null
14, 'Sports', null
15, 'Cyber Space',null
Here is sql fiddle link http://sqlfiddle.com/#!9/e9767/2
Final Query
select s1.section_id, s1.name, group_concat(s2.name) as subsections,
(select name from sections where section_id = s1.parent_section_id) as 'parentname'
from sections s1
left join sections s2 on s1.section_id = s2.parent_section_id
group by s1.section_id;
You can get the result you want by using a (left) self-join on section_id = parent_section_id like so:
select s1.section_id, s1.name, group_concat(s2.name) as subsections
from sections s1
left join sections s2 on s1.section_id = s2.parent_section_id
group by s1.section_id;
Sample SQL Fiddle