SQL - Aggregate same column through different criteria - mysql

Say I have a table Orders that looks like this,
|country| customer_id | order_id |
| CA | 5 | 3 |
| CA | 5 | 4 |
| CA | 6 | 5 |
| CA | 6 | 6 |
| US | 2 | 7 |
| US | 7 | 8 |
| US | 7 | 9 |
| US | 7 | 10 |
| US | 2 | 11 |
and I want to write a query to populate a table as so,
| country | customers_w_2_orders | customers_w_2_plus_orders |
| CA | 2 | 0 |
| US | 1 | 1 |
where it aggregates number of customers with 2 orders and number of customers with 3 orders by country.
Here's what I did and it did not give the result I want..
SELECT country, count(*) as cnt1, count(*) as cnt2
FROM Orders
GROUP BY country
HAVING cnt1=2 AND cnt2>2;

declare #orders table (country char(2), customer_id int, order_id int);
insert into #orders values
('CA', 5, 3),
('CA', 5, 4),
('CA', 6, 5),
('CA', 6, 6),
('US', 2, 7),
('US', 7, 8),
('US', 7, 9),
('US', 7, 10),
('US', 2, 11);
select country,
sum(case when num_orders <= 2 then 1 else 0 end) as cust_w_2_orders,
sum(case when num_orders > 2 then 1 else 0 end) as cust_2_plus_orders
from (
select country, customer_id, count(*) num_orders
from #orders
group by country, customer_id
) x
group by country;
GO
country | cust_w_2_orders | cust_2_plus_orders
:------ | --------------: | -----------------:
CA | 2 | 0
US | 1 | 1
dbfiddle here

First construct a table that contains every customer and the # of orders they have per country where each row is country, customer_id, number_of_orders
Now you can count how often number_of_orders is 2 or greater than 2 by grouping on the derived table
select country, sum(num_orders = 2), sum(num_orders > 2)
from (
select country, customer_id, count(*) as num_orders
from Orders
group by country, customer_id
) t group by country

SELECT country,
(select count(distinct(customer_id)) from Orders o where o.country = Orders.country and (select count(*) from Orders o2 where o2.country = orders.country and o2.customer_id = o.customer_id) = 2) as customers_w_2_orders,
(select count(distinct(customer_id)) from Orders o where o.country = Orders.country and (select count(*) from Orders o2 where o2.country = orders.country and o2.customer_id = o.customer_id) > 2) as customers_w_2_plus_orders
FROM Orders
GROUP BY country;

Related

Get min / max grouped by two columns

I have 2 tables
Parts table:
id | part_id | group_id
1 | | 1
2 | 1 | 1
3 | | 1
4 | | 2
Each part related to group and can be related to another part (analog)
Offers table:
offer_id | part_id | quantity
1 | 1 | 1
2 | 2 | 2
3 | 2 | 3
4 | 3 | 4
5 | 4 | 5
Each offer related to part
I need to get best offers grouped by part or its related part ordered by lowest / highest quantity.
For group_id 1, when ordered ASC result should be
offer_id | part_id | quantity
1 | 1 | 1
4 | 3 | 4
When ordered DESC result should be
offer_id | part_id | quantity
4 | 3 | 4
3 | 2 | 3
I tried this query
SELECT
pi.offer_id,
pi.part_id,
( SELECT pps.quantity
FROM offers AS pps
WHERE pps.offer_id = pi.offer_id
ORDER BY pps.quantity asc
LIMIT 1
) AS q
FROM offers AS pi
JOIN parts p ON p.id = pi.part_id
WHERE p.group_id = 1
GROUP BY part_id
ORDER BY q asc
Result:
offer_id | part_id | q
1 | 1 | 1
2 | 2 | 2
4 | 3 | 4
It does not group parts and related parts (1 and 2) and returns 3 rows instead of two. How do i fix it?
UPDATE
Is it possible if i change parts table data to this?
id | part_id | group_id
1 | 1 | 1
2 | 1 | 1
3 | 3 | 1
4 | 4 | 2
I also tried this query but it doesnt group by part_id too
SELECT a.id, a.part_id, a.quantity, p.part_id AS pid
FROM supplier_offers a
INNER JOIN
(
SELECT part_id, Min(quantity) AS qty
FROM supplier_offers
GROUP BY part_id
) b ON a.part_id = b.part_id AND a.quantity = b.qty
JOIN parts p ON p.id = a.part_id
WHERE p.cross_group_uuid = '78242c22-c113-4258-806c-936de014ba10'
ORDER BY a.quantity ASC
Result from real db
UPDATE 2
It seems only way is to save part part_id in offers table and group by it
I don't think that this is possible, because only with as ordering you can't achieve this.
As you can see in the fiddle example you have also to add a and p.part_id is NULL
CREATE TABLE offers
(`offer_id` int, `part_id` int, `quantity` int)
;
INSERT INTO offers
(`offer_id`, `part_id`, `quantity`)
VALUES
(1, 1, 1),
(2, 2, 2),
(3, 2, 3),
(4, 3, 4),
(5, 4, 5)
;
✓
✓
CREATE TABLE parts
(`id` int, `part_id` int, `group_id` int)
;
INSERT INTO parts
(`id`, `part_id`, `group_id`)
VALUES
(1, NULL, 1),
(2, 1, 1),
(3, NULL, 1),
(4, NULL, 2)
;
✓
✓
SELECT
o.offer_id,o.part_id, o.quantity
FROM offers o
inner JOIN parts p
ON p.id = o.part_id
WHERE group_id = 1 and p.part_id is NULL
offer_id | part_id | quantity
-------: | ------: | -------:
1 | 1 | 1
4 | 3 | 4
SELECT *
FROM
(SELECT
o.offer_id,o.part_id, o.quantity
FROM offers o
inner JOIN parts p
ON p.id = o.part_id
WHERE group_id = 1
ORDER by quantity
LIMIT 1) t1
union ALL
(SELECT
o.offer_id,o.part_id, o.quantity
FROM offers o
inner JOIN parts p
ON p.id = o.part_id
WHERE group_id = 1
ORDER by quantity DESC
LIMIT 1)
offer_id | part_id | quantity
-------: | ------: | -------:
1 | 1 | 1
4 | 3 | 4
SELECT
o.offer_id,o.part_id, o.quantity
FROM offers o
inner JOIN parts p
ON p.id = o.part_id
WHERE group_id = 1
ORDER BY o.offer_ID DESC
LIMIT 2
offer_id | part_id | quantity
-------: | ------: | -------:
4 | 3 | 4
3 | 2 | 3
SELECT
o.offer_id,o.part_id, o.quantity
FROM offers o
inner JOIN parts p
ON p.id = o.part_id
WHERE group_id = 1 and p.part_id is NULL
ORDER BY o.offer_ID ASC
LIMIT 2
offer_id | part_id | quantity
-------: | ------: | -------:
1 | 1 | 1
4 | 3 | 4
SELECT
MIN(pi.offer_id),
pi.part_id,
MIN( ( SELECT pps.quantity
FROM offers AS pps
WHERE pps.offer_id = pi.offer_id
ORDER BY pps.quantity DESC
LIMIT 1
)) AS q
FROM offers AS pi
JOIN parts p ON p.id = pi.part_id
WHERE p.group_id = 1 and p.part_id IS NULL
GROUP BY part_id
ORDER BY q asc
MIN(pi.offer_id) | part_id | q
---------------: | ------: | -:
1 | 1 | 1
4 | 3 | 4
db<>fiddle here

SQL - Find Highest Single Day of Sales From Date Range

I am using MySQL..
I have a simple sales table as follow:
o----o----------o-----------o
| id | store_id | logDate |
o----o----------o-----------o
| 1 | 1 | 2015-1-13 |
| 2 | 1 | 2015-1-14 |
| 3 | 2 | 2015-1-11 |
| 4 | 2 | 2015-1-18 |
o----o----------o-----------o
And sale product table
o----o----------o---------o------------o
| id | sale_id | qty | price |
o----o----------o---------o------------o
| 1 | 1 | 1 | 10 |
| 2 | 2 | 1 | 10 |
| 3 | 2 | 1 | 10 |
| 4 | 3 | 1 | 10 |
| 5 | 3 | 1 | 10 |
| 6 | 3 | 1 | 10 |
| 7 | 4 | 1 | 10 |
| 8 | 4 | 1 | 10 |
o----o----------o---------o------------o
Expected Result
o-- --------o----------------o---------------------o
| store_id | SUM(price*qty) | Highest Date On |
o-----------o----------------o---------------------o
| 1 | 20 | 2015-1-14 |
| 2 | 30 | 2015-1-11 |
O-----------o----------------o---------------------o
How to achieve my expected result?
I have tried as follow but it didn't work as expected:
SELECT store_id, MAX(total), highestSingleDateOn
FROM (
SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sales_id
GROUP BY DATE(s.logDate), s.store_id
ORDER BY DATE(s.logDate) ASC
) AS result_for_highest_single_day
GROUP BY highestSingleDateOn, store_id
SELECT store_id, MAX(total), highestSingleDateOn
FROM (
SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sales_id
GROUP BY DATE(s.logDate), s.store_id
ORDER BY total DESC
) AS result_for_highest_single_day
GROUP BY store_id
I just have modified the script ORDER BY DATE(s.logDate) ASC >> ORDER BY total DESC
and GROUP BY highestSingleDateOn, store_id >> GROUP BY store_id.
*Above sql script,it uses the unstable features about group by of MYSQL.
*Then according to Mysql standard,I write a other version sql script.
select table1.*
from
( SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sale_id
GROUP BY DATE(s.logDate), s.store_id) as table1
,
(select tmp.store_id,MAX(tmp.total) as max_total from
(SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sale_id
GROUP BY DATE(s.logDate), s.store_id ) as tmp group by tmp.store_id) as table2
where table1.store_id = table2.store_id and table1.total=table2.max_total
One way to do this in MySQL is with multiple an aggregations and then a join. Perhaps an easier way is to use variables:
SELECT sd.*
FROM (SELECT sd.*,
(#rn := if(#s = store_id, #rn + 1,
if(#s := store_id, 1, 1)
)
) as rn
FROM (SELECT DATE(s.logDate) AS date, s.store_id, SUM(price * qty) AS total
FROM sale_product sp JOIN sales s ON s.id = sp.sales_id
GROUP BY DATE(s.logDate), s.store_id
ORDER BY s.store_id, total desc
) sd cross join
(SELECT #rn := 0, #s := -1) params
) sd
WHERE rn = 1;

MySQL: Find day with max orders

I have a very basic orders table:
+----------+---------+------------+---------+
| order_id | cust_id | order_date | book_id |
+----------+---------+------------+---------+
| 1 | 1 | 10/10/2014 | 1 |
+----------+---------+------------+---------+
| 2 | 1 | 10/10/2014 | 2 |
+----------+---------+------------+---------+
| 3 | 1 | 10/12/2014 | 1 |
+----------+---------+------------+---------+
| 4 | 2 | 10/18/2014 | 6 |
+----------+---------+------------+---------+
| 5 | 2 | 10/18/2014 | 77 |
+----------+---------+------------+---------+
| 6 | 2 | 10/18/2014 | 103 |
+----------+---------+------------+---------+
| 7 | 2 | 10/10/2014 | 13 |
+----------+---------+------------+---------+
| 8 | 3 | 10/09/2014 | 1 |
+----------+---------+------------+---------+
| 9 | 3 | 10/11/2014 | 2 |
+----------+---------+------------+---------+
| 10 | 3 | 10/12/2014 | 3 |
+----------+---------+------------+---------+
The query should:
- For each customer show order_date with Max number of orders. Example: customer 1 had 2 orders on 10/10/2014
- Special case: When customer has multiple days with most orders, show the earliest date. Example: customer 3 had 3 single item orders on different days. Need to show 10/09/2014.
I should be able to solve this without use of temp tables.
Kira
You need two clauses: 1) find the max number of orders per customer per day and 2) find the day on which the max number of orders occurred. The second step is needed to eliminate multiple days with same number of orders.
SELECT * FROM
(SELECT cust_id,
order_date,
count() cnt
FROM
orders
GROUP BY cust_id, order_date) x
JOIN
(SELECT cust_id,
MAX(cnt) max_cnt
FROM
(SELECT cust_id,
order_date,
count() AS cnt
FROM
orders
GROUP BY cust_id, order_date) n
GROUP BY cust_id) y
ON
y.max_cnt = x.cnt
AND
y.cust_id = x.cust_id
JOIN (SELECT x.cust_id,
min(x.order_date) AS dd
FROM
(SELECT cust_id,
order_date,
count() cnt
FROM orders
GROUP BY by cust_id, order_date) x
JOIN
(SELECT cust_id,
MAX(cnt) max_cnt
FROM
(SELECT cust_id,
order_date,
count() as cnt
FROM
orders
GROUP BY
cust_id, order_date) n
GROUP BY cust_id) y
ON
y.max_cnt = x.cnt
AND
y.cust_id = x.cust_id
GROUP BY cust_id) AS t2
ON
t2.cust_id=x.cust_id
AND
t2.dd=x.order_date;

MySql LEFT JOIN with COUNT

I have two tables, customers and sales. I want to count sales for each customer and create a table of sales per month for each store.
I would like to produce something like;
------------------------------
month | customers | sales |
------------------------------
1/2013 | 5 | 2 |
2/2013 | 21 | 9 |
3/2013 | 14 | 4 |
4/2013 | 9 | 3 |
but I am having trouble getting the sales count to be correct when using the following;
SELECT CONCAT(MONTH(c.added), '/', YEAR(c.added)), count(c.id), count(s.id)
FROM customers c
LEFT JOIN sales s
ON s.customer_id = c.id AND MONTH(c.added) = MONTH(s.added) AND YEAR(c.added) = YEAR(s.added)
WHERE c.store_id = 1
GROUP BY YEAR(c.added), MONTH(c.added);
Customers table;
-------------------------------
id | store_id | added |
-------------------------------
1 | 1 |2013-02-01 |
2 | 1 |2013-02-02 |
3 | 1 |2013-03-16 |
sales table;
---------------------------------
id | added | customer_id |
---------------------------------
1 | 2013-02-18 | 3 |
2 | 2013-03-02 | 2 |
3 | 2013-03-16 | 3 |
Can anyone help here?
thanks
(Updated) The existing query will only count sales made in the same month that the customer was added. Try this, instead:
SELECT CONCAT(MONTH(sq.added), '/', YEAR(sq.added)) month_year,
sum(sq.customer_count),
sum(sq.sales_count)
FROM (select s.added, 0 customer_count, 1 sales_count
from customers c
JOIN sales s ON s.customer_id = c.id
WHERE c.store_id = 1
union all
select added, 1 customer_count, 0 sales_count
from customers
WHERE store_id = 1) sq
GROUP BY YEAR(sq.added), MONTH(sq.added);
SELECT c.* , s.sales_count<br>
FROM customers c<br>
LEFT JOIN (SELECT customer_id, count(id) as sales_count FROM sales GROUP BY customer_id) s on c.id=s.customer_id<br>
WHERE c.store_id = 1<br>

Getting COUNT while ignoring GROUP BY

I have the following table: ProductSales
+-------+-----------+--------+-----------+
|prod_id|customer_id|order_id|supplier_id|
+-------+-----------+--------+-----------+
| 1 | 1 | 1 | 1 |
+-------+-----------+--------+-----------+
| 2 | 4 | 2 | 2 |
+-------+-----------+--------+-----------+
| 3 | 1 | 1 | 1 |
+-------+-----------+--------+-----------+
| 4 | NULL | NULL | Null |
+-------+-----------+--------+-----------+
| 5 | 1 | 1 | 2 |
+-------+-----------+--------+-----------+
| 6 | 4 | 7 | 1 |
+-------+-----------+--------+-----------+
| 7 | 1 | 1 | 3 |
+-------+-----------+--------+-----------+
I have a SELECT query:
SELECT customer_id AS customer, count(*) AS prod_count
, count(DISTINCT order_id) as orders
FROM ProductSales
WHERE supplier_id=1
GROUP BY customer_id
HAVING customer_id<>'NULL'
This will be produce the result:
+--------+----------+------+
|customer|prod_count|orders|
+--------+----------+------+
| 1 | 2 | 1 |
+--------+----------+------+
| 4 | 1 | 1 |
+--------+----------+------+
What I have been trying to achieve and getting nowhere is to add a fourth column in my results to show the number of order_ids that belong only to the current supplier for each customer:
+--------+----------+------+-------------+
|customer|prod_count|orders|Unique Orders|
+--------+----------+------+-------------+
| 1 | 2 | 1 | 0 | } Order '1' is connected with two supplier_ids
+--------+----------+------+-------------+
| 4 | 1 | 1 | 1 | } Order '2' is connected to only one supplier_id
+--------+----------+------+-------------+
(This gets more complex when there are more orders per customer associated with far more suppliers).
I thought I was close with:
SELECT t1.user_id, count(DISTINCT t1.prod_id) AS prod_count
, count(DISTINCT t1.order_id) as orders
, IF(count(DISTINCT t3.supplier_id)>1,0,1) AS Unique_Orders
FROM ProductSales AS t1
LEFT JOIN `order` AS t2 ON t1.order_id=t2.order_id
LEFT JOIN ProductSales AS t3 ON t2.order_id=t3.order_id
WHERE t1.supplier_id=1
GROUP BY t1.customer_id
HAVING t1.customer_id<>'NULL'
The orders table stated above is related to ProductSales only by order_id.
Which shows my Customers, Products(total), Orders(total) but the Unique Orders shows if there are unique orders (0) or not (1), I understand the logic of the IF statement and it does what I expect. It's working out how to find the number of unique orders which is baffling me.
The table is established and can't be changed.
Any suggestions?
Unique orders can be defined as
SELECT OrderID
FROM yourtable
GROUP BY OrderID
Having COUNT(Distinct SupplierID) = 1
So try
SELECT
customer_id AS customer,
count(*) AS prod_count.
count(DISTINCT productsales.order_id) as orders,
COUNT(distinct uqo)
FROM ProductSales
left join
(
SELECT Order_ID uqo
FROM Productsales
GROUP BY Order_ID
Having COUNT(Distinct supplier_id) = 1
) uniqueorders
on ProductSales.order_id = uniqueorders.uqo
WHERE supplier_id=1
GROUP BY customer_id