Sum up query based on 2x join table - mysql

My sql looks like:
create table ad(
ad_id int,
ad_name varchar(10)
);
insert into ad(ad_id, ad_name) values
(1,'ad1'),
(2,'ad2'),
(3,'ad3');
create table ad_insight(
id int,
ad_id int,
date date,
clicks int
);
insert into ad_insight(id, ad_id, date, clicks) values
(1, 1, '2021-04-25', 1),
(2, 1, '2021-04-24', 4),
(3, 1, '2021-04-23', 2),
(4, 2, '2021-04-25', 6),
(5, 2, '2021-03-03', 7);
create table product(
product_id int,
product_name varchar(10)
);
insert into product(product_id, product_name) values
(1,'prod1'),
(2,'prod2'),
(3,'prod3'),
(4,'prod4'),
(5,'prod5');
create table product_insight(
id int,
product_id int,
sale int,
date date
);
insert into product_insight(id, product_id, sale, date) values
(1, 1, 12, '2021-04-25'),
(2, 1, 11, '2021-04-24'),
(3, 1, 13, '2021-04-23'),
(4, 1, 14, '2021-04-22'),
(5, 1, 17, '2021-04-21'),
(6, 1, 15, '2021-04-20'),
(7, 1, 13, '2021-04-19'),
(8, 2, 19, '2021-04-25');
create table ads_products(
ad_id int,
product_id int
);
insert into ads_products (ad_id, product_id) values
(1, 1),
(1, 2),
(2, 3),
(2, 4),
(1, 3);
Here you have fiddle
A quick explanation of schema:
I have ads:
each ad has insights, which tell us when a certain ad was active.
each ad has products(many2many - ads_products table). Each product has product_insight which tells us how many sales that product generated on a certain day.
And now I want to get the following table which will sum up clicks from ad_insight table and sum up product_sale from product_insight in 2021-04-23 to 2021-04-25 inclusive.
+----------+--------+--------------+--------------+
| ad_name | clicks | product_sale | products |
+----------+--------+--------------+--------------+
| ad1 | 7 | 55 | prod1, prod2 |
| ad2 | 6 | 0 | prod3, prod4 |
| ad3 | 0 | 36 | prod1 |
+----------+--------+--------------+--------------+
What I have tried?
select ad_name, SUM(ad_insight.clicks) as clicks
from ad
left join ad_insight on ad.ad_id = ad_insight.ad_id
where ad_insight.date >= '2021-04-23' and ad_insight.date <= '2021-04-25'
group by ad.ad_id;
But I do not know how to select product_sale table and products separated by a comma?

Related

MYSQL - facing difficulty combining two columns from two different tables

How can I find the restaurant name and the total number of orders for each in Jan 2021? The issue I'm facing is that the restaurant names and the orders are on separate tables as you can see from the code below.
create table orders (id integer, country text, customer_id integer,
restaurant_id INTEGER, date date, order_value integer);
create table customers (id integer, name text, country text);
create table restaurants (id integer, name text, country text);
INSERT INTO orders (
id,
country,
customer_id,
restaurant_id,
date,
order_value)
VALUES
(1, 'Pakistan', 1, 1, '2021-01-01', 400),
(2, 'Pakistan', 2, 1, '2021-01-01', 500),
(3, 'Pakistan', 4, 2, '2021-01-01', 300),
(4, 'Pakistan', 4, 3, '2021-01-05', 200),
(5, 'Pakistan', 5, 4, '2021-01-01', 250),
(6, 'Pakistan', 4, 1, '2021-01-09', 266),
(7, 'Pakistan', 3, 2, '2021-01-07', 322),
(1, 'Holland', 1, 1, '2021-01-01', 378),
(8, 'Pakistan', 1, 3, '2021-06-01', 289),
(2, 'Holland', 1, 1, '2021-08-01', 480),
(9, 'Pakistan', 1, 1, '2021-03-01', 580),
(10, 'Pakistan', 3, 2, '2021-07-01', 360),
(3, 'Holland', 1, 1, '2021-09-01', 550),
(11, 'Pakistan', 4, 3, '2021-04-01', 991),
(12, 'Pakistan', 5, 1, '2021-04-01', 875),
(4, 'Holland', 1, 1, '2021-03-02', 250),
(13, 'Pakistan', 1, 1, '2021-08-01', 150),
(14, 'Pakistan', 1, 2, '2021-09-01', 290),
(5, 'Holland', 1, 1, '2021-07-01', 240),
(15, 'Pakistan', 1, 3, '2021-03-01', 780),
(16, 'Pakistan', 1, 4, '2021-06-01', 987),
(6, 'Holland', 1, 1, '2021-05-03', 457),
(17, 'Pakistan', 1, 4, '2021-05-04', 258);
INSERT INTO customers (
id,
name,
country)
VALUES
(1, 'Steven Smith', 'Pakistan'),
(2, 'Arthur Chen', 'Holland'),
(3, 'Michael Wren', 'Pakistan'),
(4, 'John Almagro', 'Pakistan'),
(5, 'Luke Pablo', 'Pakistan'),
(6, 'Monty Tron', 'Pakistan');
INSERT INTO restaurants (
id,
name,
country)
VALUES
(1, 'KFC', 'Pakistan'),
(2, "McDonald's", 'Holland'),
(3, 'Howdy', 'Pakistan'),
(4, 'Kitchen Cuisine', 'Pakistan'),
(5, 'JFC', 'Pakistan'),
(6,'Hardees','Pakistan');
I learned about JOIN functions but I'm not able to join the dots.
Joining two table, is made by telling the database, which rows belong together. this is defined in the ON clause, where the joning columns are mentioned.
the WHERE clause is the same as in the last query it removes all rows that have not the right year and month.
The Group By has here three columns, because the restaurant_id has always the same value. We could also had added a aggregation function to the columns, which would have the same effect
SELECT
r.name,
r.country,
COUNT(*) Total_orders
FROM
orders o JOIN restaurants r ON o.restaurant_id = r.id
WHERE YEAR(`date`)= 2021 AND MONTH(`date`)= 1
GROUP BY restaurant_id,r.name,r.country
name | country | Total_orders
:-------------- | :------- | -----------:
KFC | Pakistan | 4
McDonald's | Holland | 2
Howdy | Pakistan | 1
Kitchen Cuisine | Pakistan | 1
SELECT
MAX(r.name) name,
MAX(r.country) country,
COUNT(*) Total_orders
FROM
orders o JOIN restaurants r ON o.restaurant_id = r.id
WHERE YEAR(`date`)= 2021 AND MONTH(`date`)= 1
GROUP BY restaurant_id
name | country | Total_orders
:-------------- | :------- | -----------:
KFC | Pakistan | 4
McDonald's | Holland | 2
Howdy | Pakistan | 1
Kitchen Cuisine | Pakistan | 1
db<>fiddle here
SELECT -- MySQL SELECT statement
MAX(r.name) `Hotel Name`, -- column name as Hotel Name from table restaurants, alias as r
COUNT(*) `Number Of Orders`, -- count all the records for table orders, alias o
SUM(o.order_value) `Total Order Value`, -- SUM all the order_values for match records
MAX(r.country) `Country` -- column country
FROM
orders o -- Running operations on table orders and set alias o
INNER JOIN restaurants r ON o.restaurant_id = r.id -- INNER JOIN second table restaurants as r and joining
-- two tables using field r.id (restaurants primary key id)
-- and o.restaurant_id (foreign key of restaurant's primary key id)
WHERE -- setting condition
YEAR(o. `date`) = 2021 -- Year must be 2021
AND MONTH(o. `date`) = 1 -- and month must be JAN or 1
GROUP BY -- group all same hotels id
o.restaurant_id;
| Hotel Name | Number Of Orders | Total Order Value | Country |
|-----------------|------------------|-------------------|----------|
| KFC | 4 | 1544 | Pakistan |
| McDonald's | 2 | 622 | Holland |
| Howdy | 1 | 200 | Pakistan |
| Kitchen Cuisine | 1 | 250 | Pakistan |
HINT -
To join the two different tables we'd need two columns which has same values, common or has some linking.
Here table orders has restaurant_id which is a foreign key of table restaurants (id). In other word, we can use those id to identify the restaurant details by querying table restaurants.
hence to join table orders and restaurants, we should use column id from table restaurants and column restaurant_id from table orders.
Now since orders table has multiple rows with the same restaurant_id; it's better group them together to make as buckets.
Once we use GROUP BY a column; MySQL group them in a bucket which has same values or given conditions.
Any aggregated statement like SUM, AVG, COUNT, MAX, MIN, etc. would take those individual buckets as logical table and perform the operations.

Sql sum up based on parent table

My sql looks like:
create table ad(
ad_id int,
ad_name varchar(10)
);
insert into ad(ad_id, ad_name) values
(1,'ad1'),
(2,'ad2'),
(3,'ad3');
create table ad_insight(
id int,
ad_id int,
date date,
clicks int
);
insert into ad_insight(id, ad_id, date, clicks) values
(1, 1, '2021-04-25', 1),
(2, 1, '2021-04-24', 4),
(3, 1, '2021-04-23', 2),
(4, 2, '2021-04-25', 6),
(5, 2, '2021-03-03', 7);
create table product(
product_id int,
ad_id int,
product_name varchar(10)
);
insert into product(product_id, ad_id, product_name) values
(1, 1, 'prod1'),
(2, 1, 'prod2'),
(3, 2, 'prod3'),
(4, 2, 'prod4');
(1, 3, 'prod1');
create table product_insight(
id int,
product_id int,
sale int,
date date
);
insert into product_insight(id, product_id, sale, date) values
(1, 1, 12, '2021-04-25'),
(2, 1, 11, '2021-04-24'),
(3, 1, 13, '2021-04-23'),
(4, 1, 14, '2021-04-22'),
(5, 1, 17, '2021-04-21'),
(6, 1, 15, '2021-04-20'),
(7, 1, 13, '2021-04-19'),
(8, 2, 19, '2021-04-25');
Here you have fiddle
A quick explanation of schema:
I have ads:
each ad has insights, which tell us when a certain ad was active.
each ad has products. Each product has product_insight which tells us how many sales that product generated on a certain day.
And now I want to get the following tables:
which will sum up clicks from ad_insight table and sum up product_sale from product_insight in 2021-04-23 to 2021-04-25 inclusive.
+----------+--------+--------------+--------------+
| ad_name | clicks | product_sale | products |
+----------+--------+--------------+--------------+
| ad1 | 7 | 55 | prod1, prod2 |
| ad2 | 6 | 0 | prod3, prod4 |
| ad3 | 0 | 36 | prod1 |
+----------+--------+--------------+--------------+
The summary row which will sum up everything in the above table:
+------------+--------------+--------------------+----------------------------+
| total_ads | total_clicks | total_product_sale | unique_all_products |
+------------+--------------+--------------------+----------------------------+
| 3 | 13| 91 | prod1, prod2, prod3, prod4 |
+------------+--------------+--------------------+----------------------------+
What I have tried?
# 1) table
select ad_name, SUM(ad_insight.clicks) as clicks
from ad
left join ad_insight on ad.ad_id = ad_insight.ad_id
where ad_insight.date >= '2021-04-23' and ad_insight.date <= '2021-04-25'
group by ad.ad_id;
# 2) table
select count(distinct ad_insight.ad_id) as total, SUM(ad_insight.clicks) as clicks
from ad_insight
left join ad on ad.ad_id = ad_insight.ad_id
where ad_insight.date >= '2021-04-23' and ad_insight.date <= '2021-04-25'
But I do not know how select product_sale table and products separated by comma!
If I understand correctly, you want to aggregate along two different dimensions (clicks and sales) for each ad. Aggregate before joining:
select ad.ad_name, ai.clicks, p.sales, p.products
from ad left join
(select ai.ad_id, sum(ai.clicks) as clicks
from ad_insight ai
where ai.date >= '2021-04-23' and ai.date <= '2021-04-25'
group by ai.ad_id
) ai
on ad.ad_id = ai.ad_id left join
(select p.ad_id, sum(pi.sale) as sales,
group_concat(distinct p.product_name) as products
from product p join
product_insight pi
on pi.product_id = p.product_id
where pi.date >= '2021-04-23' and pi.date <= '2021-04-25'
group by p.ad_id
) p
on p.ad_id = ad.ad_id ;
The second query just aggregates this again.

MySQL Update a table by min of another table

I would like to update Table Lease from Table History
CREATE TABLE Lease
(`LeaseID` int, `Name` varchar(3), `Users` varchar(15), `WhoSignID` int, `NoteDate` date)
;
INSERT INTO Lease
(`LeaseID`, `Name`, `Users`, `WhoSignID`, `NoteDate`)
VALUES
(1, 'AAA', '1000,1001', NULL, NULL),
(2, 'BBB', '1002', NULL, NULL),
(3, 'CCC', '1003,1004', NULL, NULL),
(4, 'DDD', '1005,1006, 1007', NULL, NULL)
;
CREATE TABLE History
(`HistoryID` int, `LeaseID` int, `User` int, `SignDate` date)
;
INSERT INTO History
(`HistoryID`, `LeaseID`, `User`, `SignDate`)
VALUES
(1, 1, 1000, '2020-01-05'),
(2, 1, 1001, '2020-01-04'),
(3, 1, 1001, '2020-01-02'),
(4, 1, 1000, '2020-01-03'),
(6, 2, 1002, '2020-05-01'),
(7, 2, 1002, '2020-05-03')
;
I looking of a Mysql Update to update Table Lease :
NoteDate and WhoSignID based on SignDate and User
where Minimum of SignDate of User
Table Lease After Update
LeaseID | Name | Users | WhoSignID | NoteDate
1 | AAA | 1000,1001 | 1001 | 2020-01-02
2 | BBB | 1002 | 1002 | 2020-05-01
...
I appreciate any assist
Your Lease table has a serious design problem, because it is storing users as a CSV list. Instead, you should have each user value on a separate record. That being said, it appears that the CSV user list is immaterial to your current problem, which only required finding the earliest date for each lease. If so, then a simple update join should suffice:
UPDATE Lease l
INNER JOIN
(
SELECT h1.LeaseID, h1.User, h2.MinSignDate
FROM History h1
INNER JOIN
(
SELECT LeaseID, MIN(SignDate) AS MinSignDate
FROM History
GROUP BY LeaseID
) h2
ON h2.LeaseID = h1.LeaseID AND
h2.MinSignDate = h1.SignDate
) h
ON h.LeaseID = l.LeaseID
SET
WhoSignID = h.User,
NoteDate = h.MinSignDate;

Select products and join categories hierarchical

I have two tables in my database:
create table category (id integer, name text, parent_id integer);
create table product (id integer, name text, category integer, description text);
insert into category
values
(1, 'Category A', null),
(2, 'Category B', null),
(3, 'Category C', null),
(4, 'Category D', null),
(5, 'Subcategory Of 1', 1),
(6, 'Subcategory Of 5', 5),
(7, 'Subcategory Of 5', 5),
(8, 'Subcategory of D', 4)
;
insert into product
values
(1, 'Product One', 5, 'Our first product'),
(2, 'Product Two', 6, 'Our second product'),
(3, 'Product Three', 8, 'The even better one');
How can I return like this:
product_id | product_name | root_category | category_path
-----------+--------------+---------------+-----------------------------
1 | Product One | 1 | /Category A/Subcategory Of 1
2 | Product Two | 1 | /Category A/Subcategory of 5/Subcategory of 6
I use "WITH RECURSIVE" in categories table but can't find the way to combine product table with 1 time query.
I use example from here
What's the best way to do this ?
Here you go, assumming you have MariaDB 10.2 or newer:
with recursive pt (root_id, id, path) as (
select id, id, concat('/', name) from category where parent_id is null
union all
select pt.root_id, c.id, concat(pt.path, '/', c.name)
from pt join category c on c.parent_id = pt.id
)
select p.id, p.name, pt.root_id, pt.path
from pt
join product p on pt.id = p.category;
Result:
id name root_id path
-- -------------- ------- ---------------------------------------------
1 Product One 1 /Category A/Subcategory Of 1
2 Product Two 1 /Category A/Subcategory Of 1/Subcategory Of 5
3 Product Three 4 /Category D/Subcategory of D

Joining 2 tables with Group By

poll_opts table for storing options and poll_voted for storing vote result, pid stands for poll id(unique) and oid stands for option id(unique for individual poll only)
poll_voted [Primary Key: pid.oid.emp]
+-----+-----+-------+
| pid | oid | emp |
+-----+-----+-------+
poll_opts [Primary Key: pid.oid]
+-----+-----+---------+
| pid | oid | opt |
+-----+-----+---------+
pid & oid type: int , opt type: text
If you need the "not existent" results as well you need a left outer join preserves all results from poll_opts even if no match in poll_votes is found.
MySql 5.7 Join Syntax
Query:
select opt, count(vo.oid)
from poll_opts po
left outer join poll_voted vo on vo.oid = po.oid and po.pid=vo.pid
where po.pid = 3 -- 3
group by opt
Output:
opt count(vo.oid)
Chrome 0
Firefox 0
IE 0
MS Edge 0
Opera 1
Testdata:
CREATE TABLE poll_voted (`pid` int, `oid` int, `emp` int);
INSERT INTO poll_voted (`pid`, `oid`, `emp`)
VALUES
(1, 0, 1989),
(1, 2, 1989),
(1, 4, 1989),
(1, 6, 1989),
(3, 2, 1989)
;
CREATE TABLE poll_opts (`pid` int, `oid` int, `opt` varchar(15));
INSERT INTO poll_opts (`pid`, `oid`, `opt`)
VALUES
(1, 0, 'WinXP'),
(1, 2, 'wIN7'),
(1, 4, 'wIN 10'),
(1, 6, 'Ubuntu'),
(3, 0, 'IE'),
(3, 1, 'MS Edge'),
(3, 2, 'Opera'),
(3, 3, 'Chrome'),
(3, 4, 'Firefox')
;