MySQL Count and SUM from second table with group by - mysql

I'm trying to get sales and quantity sale by crossing two tables, group by the first one and sum from the second one.
First table has sales/operations: id_sales, sales_rep
Second table has sales details: id_sales_details, id_sales, quantity
What I need to know is how many operations had each sales_rep and what was the total quantity sum of all those sales.
This MySQL query gives me the first part:
SELECT sales.sales_rep, count(*) AS sales
from sales
Group by sales_rep
Order by sales DESC
What I cannot solve is how to add to that query the second part I need. The result should look something like:
sales_rep sales quantity
Claire 4 13
Peter 2 18
Mary 1 8
John 1 7
Here's a Fiddle to make things clearer: http://sqlfiddle.com/#!9/708234/5

SELECT s.sales_rep, count(*) AS operations, sum(d.quantity)
from sales s, sales_details d
where s.id_sales = d.id_sales
Group by s.sales_rep
Order by operations DESC;

Quick solution
SELECT w.sales_rep, w.sales, SUM(quantity) as quantity
FROM
(SELECT s.sales_rep, t.sales,d.quantity FROM sales AS s
INNER JOIN sales_details AS d ON s.id_sales = d.id_sales
INNER JOIN
(SELECT sales_rep, count(*) AS sales
from sales
Group by sales_rep
Order by sales DESC ) AS t
ON s.sales_rep = t.sales_rep) AS w
GROUP BY w.sales_rep, w.sales
ORDER BY w.sales_rep ASC

Related

SQL Consecutive Monthly Purchases

I'm having great difficulty writing this query and cannot find any answers online which could be applied to my problem.
I have a couple of tables which looks similar to the below with. Each purchase date corresponds with an item purchased.
Cust_ID
Purchase_Date
123
08/01/2022
123
08/20/2022
123
09/05/2022
123
10/08/2022
123
12/25/2022
123
01/26/2023
The result I am looking for should contain the customers ID, a range of the purchases, the number of consecutive months they had made a purchase (regardless of which day they purchased), and a count of how many purchases they had made in the time frame. The result should look something like the below for my example.
Cust_ID
Min Purchase Date
Max Purchase Date
Consecutive Months
No. Items Purchased
123
08/01/2022
10/08/2022
3
4
123
12/25/2022
01/26/2023
2
2
I have tried using CTEs with querys similar to
WITH CTE as
(
SELECT
PaymentDate PD,
CustomerID CustID,
DATEADD(m, -ROW_NUMBER() OVER (PARTITION BY c.CustomerID ORDER BY
DATEPART(m,PaymentDate)), PaymentDate) as TempCol1,
FROM customers as c
LEFT JOIN payments as p on c.customerid = p.customerid
GROUP BY c.CustomerID, p.PaymentDate
)
SELECT
CustID,
MIN(PD) AS MinPaymentDate,
MAX(PD) AS MaxPaymentDate,
COUNT(*) as ConsecutiveMonths,
FROM CTE
GROUP BY CustID, TempCol1
However, the above failed to properly count consecutive months. When the payment dates matched a month apart (e.g. 1/1/22 - 2/1/22), the query properly counts the consecutive months. However, if the dates do not match from month to month (e.g. 1/5/22 - 2/15/22), the count breaks.
Any guidance/help would be much appreciated!
This is just a small enhancement on the answer already given by ahmed. If your date range for this query is more than a year, then year(M.Purchase_Date) + month(M.Purchase_Date) will be 2024 for both 2022-02-01 and 2023-01-01 as YEAR() and MONTH() both return integer values. This will return incorrect count of consecutive months. You can change this to use CONCAT() or FORMAT(). Also, the COUNT(*) for ItemsPurchased should be counting the right hand side of the join, as it is a LEFT JOIN.
WITH consecutive_months AS
(
SELECT *,
DATEADD(
month,
-DENSE_RANK() OVER (
PARTITION BY CustomerID
ORDER BY YEAR(PaymentDate), MONTH(PaymentDate)
),
PaymentDate
) AS grp_date
FROM payments
)
SELECT
C.CustomerID AS CustID,
MIN(M.PaymentDate) AS MinPaymentDate,
MAX(M.PaymentDate) AS MaxPaymentDate,
COUNT(DISTINCT FORMAT(M.PaymentDate, 'yyyyMM')) AS ConsecutiveMonths,
COUNT(M.CustomerID) AS ItemsPurchased
FROM customers C
LEFT JOIN consecutive_months M
ON C.CustomerID = M.CustomerID
GROUP BY C.CustomerID, YEAR(M.grp_date), MONTH(M.grp_date)
Here's a db<>fiddle
You need to use the dense_rank function instead of the row_number, this will give the same rank for the same months and avoid breaking the grouping column. Also, you need to aggregate for 'year-month' of the grouping date column.
with consecutive_months as
(
select *,
Purchase_Date - interval
dense_rank() over (partition by Cust_ID order by year(Purchase_Date), month(Purchase_Date))
month as grp_date
from payments
)
select C.Cust_ID,
min(M.Purchase_Date) as MinPurchaseDate,
max(M.Purchase_Date) as MaxPurchaseDate,
count(distinct year(M.Purchase_Date), month(M.Purchase_Date)) as ConsecutiveMonthsNo,
count(M.Cust_ID) as ItemsPurchased
from customers C left join consecutive_months M
on C.Cust_ID = M.Cust_ID
group by C.Cust_ID, year(M.grp_date), month(M.grp_date)
See demo on MySQL
You tagged your question with MySQL, while it seems that you posted an SQL Server query syntax, for SQL Server just use dateadd(month, -dense_rank() over (partition by Cust_ID order by year(Purchase_Date), month(Purchase_Date)), Purchase_Date).
See demo on SQL Server.

How to solve this MySQL COUNT query?

Three table are given:
customer(cust_id, name, address, sales_id)
orders(order_id, cust_id,date, sales_id)
salesman(sales_id,commision)
and you have to write an MySQL query to "count the salesman by their order_id and date". Is the question is correct? if yes, how can I solve this.
The question is not clear, but the codes below will give you an idea of how to get aggregated results and you can play around / adjust based on your needs
select
date,
sales_id,
count(order_id) as total_orders_per_date_and_sales_id
from orders
group by 1,2
if you need the total salesman per date then you can do this:
select
date,
count(sales_id) as total_sales_man_per_date
from orders
group by 1
if one order has multiple sales owners, then you can find total salesman per order id
select
orders_id,
count(salesman) as total_sales_man_involved_per_order
from orders
group by 1
if you need total commission per salesman per date:
select
orders.date,
orders.sales_id,
sum(salesman.comission) as total_comssion_per_salesman_per_date
from orders
left join salesman
on orders.sales_id = salesman.sales_id
group by 1,2
total number of distinct salesman
select
count(distinct sales_id) as total_unique_salesman
from salesman

Find customers with similar tastes while excluding certain customers

I have a table documenting purchases from customers, with one row per purchase:
CustomerID | ProductID
1 | 1000
1 | 2000
1 | 3000
2 | 1000
3 | 1000
3 | 3000
... | ...
I am using the following code to find the ten customers with the greatest number of overlapping products with customer #1 (first result is the one with the most overlap etc):
SELECT othercustomers.CustomerID, COUNT(DISTINCT othercustomers.ProductID)
FROM `purchases` AS thiscustomer
JOIN `purchases` AS othercustomers ON
thiscustomer.CustomerID != othercustomers.CustomerID
AND thiscustomer.ProductID = othercustomers.ProductID
WHERE thiscustomer.CustomerID = '1'
GROUP BY othercustomers.CustomerID
ORDER BY COUNT(DISTINCT othercustomers.ProductID) DESC
LIMIT 10
The code yields the expected output (Customer ID + total number of overlapping products with customer #1).
I would now like the query to exclude customers with overlapping purchases who have purchased more than 1000 different products, because these are bulk buyers who purchase the entire stock and whose purchase history therefore has no meaning when searching for customers with a similar taste.
In other words, if customer #500 had bought >1000 different products, I want him/her excluded from the results when searching for customers with a similar taste to that of customer #1 - even if customer #500 has bought all three products that customer #1 had bought and would ordinarily rank first in similarity/overlap.
I suppose some HAVING is in order, but I cannot seem to figure out what the appropriate condition is.
Thanks!
I think that HAVING won't do what you want, since it will only give you the total count of overlaping products, while you want the total count of products for the other customer.
You could filter with a correlated subquery in the WHERE clause:
SELECT othercustomers.CustomerID, COUNT(DISTINCT othercustomers.ProductID)
FROM `purchases` AS thiscustomer
JOIN `purchases` AS othercustomers ON
thiscustomer.CustomerID != othercustomers.CustomerID
AND thiscustomer.ProductID = othercustomers.ProductID
WHERE
thiscustomer.CustomerID = '1'
AND (
SELECT COUNT(DISTINCT ProductID)
FROM `purchases` AS p
WHERE p.CustomerID = othercustomers.CustomerID
) < 1000
GROUP BY othercustomers.CustomerID
ORDER BY COUNT(DISTINCT othercustomers.ProductID) DESC
LIMIT 10
For performance, you want an index on purchases(CustomerID, ProductID).

How to get rows with max date when grouping in MySQL?

I have a table with prices and dates on product:
id
product
price
date
I create a new record when price change. And I have a table like this:
id product price date
1 1 10 2014-01-01
2 1 20 2014-02-17
3 1 5 2014-03-28
4 2 25 2014-01-05
5 2 12 2014-02-08
6 2 30 2014-03-12
I want to get last price for all products. But when I group with "product", I can't get a price from a row with maximum date.
I can use MAX(), MIN() or COUNT() function in request, but I need a result based on other value.
I want something like this in final:
product price date
1 5 2014-03-28
2 30 2014-03-12
But I don't know how. May be like this:
SELECT product, {price with max date}, {max date}
FROM table
GROUP BY product
Alternatively, you can have subquery to get the latest get for every product and join the result on the table itself to get the other columns.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT product, MAX(date) mxdate
FROM tableName
GROUP BY product
) b ON a.product = b.product
AND a.date = b.mxdate
I think the easiest way is a substring_index()/group_concat() trick:
SELECT product,
substring_index(group_concat(price order by date desc), ',', 1) as PriceOnMaxDate
max(date)
FROM table
GROUP BY product;
Another way, that might be more efficient than a group by is:
select p.*
from table t
where not exists (select 1
from table t2
where t2.product = t.product and
t2.date > t.date
);
This says: "Get me all rows from the table where the same product does not have a larger date." That is a fancy way of saying "get me the row with the maximum date for each product."
Note that there is a subtle difference: the second form will return all rows that on the maximum date, if there are duplicates.
Also, for performance an index on table(product, date) is recommended.
You can use a subquery that groups by product and return the maximum date for every product, and join this subquery back to the products table:
SELECT
p.product,
p.price,
p.date
FROM
products p INNER JOIN (
SELECT
product,
MAX(date) AS max_date
FROM
products
GROUP BY
product) m
ON p.product = m.product AND p.date = m.max_date
SELECT
product,
price,
date
FROM
(SELECT
product,
price,
date
FROM table_name ORDER BY date DESC) AS t1
GROUP BY product;

Real world total sales SQL query

I'm fairly new to SQL and am having difficulty solving a problem.
'What are the total sales across all products for the salespeople that sell at least one unit of each of the five individual products with the highest sales by unit? Make sure that the query returns the total sales dollars in descending order. Only consider sales that take place over the six complete months prior to a #target_date parameter.'
3 tables exist in the DB.
SalesPerson (SalesPersonID,SalesYTD)
SalesOrderHeader (SalesOrderID,OrderDate,ShipDate)
SalesOrderDetail (SalesOrderID,SalesOrderDetailID,OrderQty,ProductID,UnitPrice)
This is where I'm at so far. I need to compile what I have into one statement and make necessary revisions. Please help!
To capture the top 5 highest sales by unit, the following SYNTAX should work:
SELECT
ProductID,
SUM(Orderqty*Unitprice)
FROM SalesOrderDetail
GROUP BY ProductID
WHERE Orderqty >=1
AND COUNT(productID) =5
ORDER BY SUM(Orderqty*Unitprice) DESC
LIMIT 5;
For the target_date parameter, I think it would be something along these lines?
SELECT
SalespersonID AS ‘Sales Representative’,
SalesYTD AS ‘Total Sales’, target_date
FROM Salesperson
WHERE target_date BETWEEN ‘01-DEC-13’ AND ’01-May-14’;
For the top five highest sales, I would rather propose the slightly simplified
select productid, sum(orderqty * unitprice) as sales
from salesorderdetail
group by productid
order by sales desc
limit 5
and for the six months prior to #target_date something like
where orderdate between date_sub(#target_date, interval 6 months) and #target_date
Assuming a FK SalesOrderDetail(SalesPersonID), you can then join the tables and top five sales as
select p.*
from salesperson p
join salesorderheader h on h.salespersionid = p.salespersionid
join salesorderdetail d on d.salesorderid = h.salesorderid
join (select productid, sum(orderqty * unitprice) as sales
from salesorderdetail
group by productid
order by sales desc
limit 5) t5 on t5.productid = d.productid
where h.orderdate between date_sub(#target_date, interval 6 months) and #target_date
order by p.salesytd desc