I have a MySQL table like this
Transaction(transaction_id, seller_id, sale_date)
where sale_date is a timestamp.
I would like find out the number of sales each seller has made in each month of the last three months. I composed my query like this:
SELECT seller_id, COUNT(*) as num_of_sales
FROM Transaction
WHERE sale_date > '2014-11-30 23:59:59'
GROUP BY seller_id, MONTH(sale_date)
and I am certain that the result is invalid since I got back:
seller_id num_of_sales
2 4829
5 148
and there is no such seller with id 5 in my seller table. Could anyone point me to the right direction, please? Thank you!
Here is a working query that I managed to come up with, hope it is useful for someone:
SELECT
MONTHNAME(sale_date) as d_month,
seller_id,
COUNT(*) as num_of_sales
FROM Transaction
GROUP BY seller_id, d_month
ORDER seller_id, MONTH(sale_date) asc
Related
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.
I have a very simple table which consists of the following columns:
id | customer_id | total | created_at
I was running this query to get the results per day for the last ten days:
SELECT SUM(total) AS total, DATE_FORMAT(created_at, "%d/%m/%Y") AS date
FROM table
WHERE created_at BETWEEN "2017-02-20" AND "2017-03-01"
GROUP BY created_at
ORDER BY created_at DESC
This works fine, but I've just noticed that there's an issue with imported rows being duplicated for some reason so I'd like to update the query to be able to handle the situation if it ever happens again, in other words select one row instead of all when the date and customer id are the same (the total is also identical).
If I add customer_id to the group by that seems to work but the trouble with that is then the query returns a result per day for each customer when I only want the overall total.
I've tried a couple of things but I haven't cracked it yet, I think it will be achievable using a sub query and/or an inner join, I have tried this so far but the figures are very wrong:
SELECT
created_at,
(
SELECT SUM(total)
FROM table test
WHERE test.created_at = table.created_at
AND test.customer_id = table.customer_id
GROUP BY customer_id, created_at
LIMIT 1
) AS total
FROM table
WHERE created_at BETWEEN "2017-02-20" AND "2017-03-01"
GROUP BY created_at
ORDER BY created_at DESC
It's also a large table so finding a performant way to do this is also important.
First, are you sure that created_at is a date and not a datetime? This makes a big difference.
You can do what you want using two levels of aggregation:
SELECT SUM(max_total) AS total, DATE_FORMAT(created_at, '%d/%m/%Y') AS date
FROM (SELECT t.customer_id, t.created_at, MAX(total) as max_total
FROM table t
WHERE t.created_at BETWEEN '2017-02-20' AND '2017-03-01'
GROUP BY t.customer_id, t.created_at
) t
GROUP BY created_at
ORDER BY created_at DESC;
I have a situation where I need to find the number of days that have passed between two rows with date fields. The rows that the calculation needs to be made are not sorted.
Here is the structure of the table
Folio DATE
1 6/1/2015
2 4/1/2015
1 3/1/2015
4 2/1/2015
1 1/1/2015
Basically, I would need to sort by date and keep only the last two transactions grouped by folio. so in this example, the transaction by folio 1 on 1/1/2015 would be ignored.
Suppose that I need to do the following:
1. Group by folio number
2. only count the days between the last two transactions by folio. For example, folio #1 would only include the transactions from 6/1/2015 and 3/1/2015.
The result I'm looking for:
Folio FirstDATE LastDate #ofDays
1 3/1/2015 6/1/2015 90
Any MySQL pros out there? My skills are still in newbie territory. Thank you!
UPDATE:
I've managed to come up with the following:
SELECT
SubQuery.`Folio Number`,
SubQuery.LatestClosing,
SubQuery.FirstClosing,
DATEDIFF(SubQuery.LatestClosing, SubQuery.FirstClosing) AS numofdays
FROM (SELECT
Subquery.`Folio Number`,
SubQuery.LatestClosing,
SubQuery.FirstClosing
FROM (SELECT t.`Folio Number`,
MAX(t.`Closing Date`) AS LatestClosing,
(SELECT
s.`Closing Date`
FROM MLSFinalimport s
WHERE t.`Folio Number` = s.`Folio Number`
ORDER BY s.`Closing Date` DESC
LIMIT 1, 1) AS FirstClosing,
FROM MLSFinalimport t
GROUP BY t.`Folio Number`) SubQuery) SubQuery
This is generating a result that looks like this:
LatestClosing First Closing numofdays
7/20/2016 5/9/2006 3725
This is what I need. However, I'm stuck trying to add the original column for each row called "Folio Number". How do I proceed?
Thank you very much.
Pros for MySQL at this? Probably the opposite.. MySQL doesn't support window functions so you can try using a correlated query with LIMIT/OFFSET:
SELECT p.folio,p.max_d,p.second_d,DATEDIFF(p.max_d,p.second_d) as NumOfDays
FROM (
SELECT t.folio,MAX(t.date) as max_d,
(SELECT s.date FROM YourTable s
WHERE t.folio = s.folio
ORDER BY s.date DESC
LIMIT 1,1) as second_d
FROM YourTable t
GROUP BY t.folio) p
I have a table that has a unique key each time a user creates a case:
id|doctor_id|created_dt
--|---------|-----------
1|23 |datetimestamp
2|23 |datetimestamp
3|17 |datetimestamp
How can I select and return the average amount of entries a user has per month?
I have tried this:
SELECT avg (id)
FROM `cases`
WHERE created_dt BETWEEN DATE_SUB(CURDATE(),INTERVAL 90 DAY) AND CURDATE()
and doctor_id = 17
But this returns a ridiculously large value that cannot be true.
To clarify: I am trying to get something like doctor id 17 has an average of 2 entries per month into this table.
I think you were thrown off by the idea of "averaging". You don't want the average id, or average user_id. You want the average number of entries into the table, so you would use COUNT():
SELECT count(id)/3 AS AverageMonthlyCases
FROM `cases`
WHERE created_dt BETWEEN DATE_SUB(CURDATE(),INTERVAL 90 DAY) AND CURDATE()
group by doctor_id
Since you have a 90 day interval, you want to count the number of rows per 30 days, or the count/3.
SELECT AVG(cnt), user_id
FROM (
SELECT COUNT(id) cnt, user_id
FROM cases
WHERE created_dt BETWEEN <yourDateInterval>
GROUP BY user_id, year(created_dt), month(created_dt)
)
Since you need average number of entries, AVG function is not really applicable, because it is SUM()/COUNT() and obviously you do not need that (why would you need SUM of ids).
You need something like this
SELECT
doctor_id,
DATE(created_dt,'%m-%Y') AS month,
COUNT(id) AS visits
FROM `cases`
GROUP BY
`doctor_id`,
DATE(created_dt,'%m-%Y')
ORDER BY
`doctor_id` ASC,
DATE(created_dt,'%m-%Y') ASC
To get visits per month per doctor. If you want to average it, you can then use something like
SELECT
doctor_id,
SUM(visits)/COUNT(month) AS `average`
FROM (
SELECT
doctor_id,
DATE(created_dt,'%m-%Y') AS month,
COUNT(id) AS visits
FROM `cases`
GROUP BY
`doctor_id`,
DATE(created_dt,'%m-%Y')
ORDER BY
`doctor_id` ASC,
DATE(created_dt,'%m-%Y') ASC
) t1
GROUP BY
doctor_id
Obviously you can add your WHERE clauses, as this query is compatible for multiple years (i.e. it will not count January of 2013th and January of 2014th as one month).
Also, it takes into account if a doctor has "blank" months, where he did not have any patients, so it will not count those months (0 can destroy and average).
Use this, you'll group each doctor's total id, by month.
Select monthname(created_dt), doctor_id, count(id) as total from cases group by 1,2 order by 1
Also you can use GROUP_CONCAT() as nested query in order to deploy a pivot like table, where each column is each doctor_id.
I need some help with one of the questions from my homework, Ive been trying for about an hour now and cant get it to run.
List the customers that bought more items than the average number of items per customer
The tables are as follows:
Customer(Cnum, CustomerName, Address)
Item(Inum, ItemName, Manufacturer, Year)
Bought(Cnum, Inum, Date, Quantity)
Prefer(Inum, Cnum)
The best i could figure out was that it needs to be the total Quantity per Customer compared to the overall average of the Quantity. I've tried various forms of this query:
SELECT Cnum
FROM Bought
WHERE
(
SELECT Cnum, SUM(Quantity)
FROM Bought
GROUP BY Cnum;
) >
(
SELECT AVG(Quantity)
FROM Bought
);
But it returns an error -- (phpMyAdmin isnt telling me what the problem is, just failing to execute and going to no connection page, which means error in my query)
I have also tried to return the higher SUM with:
SELECT SUM(Quantity)
FROM Bought
WHERE SUM(Quantity) > AVG(Quantity);
And same issue.
Any help would be appreciated, even an explanation as to why the second one fails.
You might want to take a look at the HAVING clause of SQL.
Note: I'm intentionally not giving you the full answer since this is homework.
why Don't you try this.
SELECT `Cnum` , Sum( Quantity )
FROM `bought`
GROUP BY `Cnum`
HAVING Sum( Quantity ) > ( SELECT AVG( Quantity ) FROM Bought )
Try below:
SELECT Cnum
FROM Bought
having SUM(Quantity) > (SELECT avg(Quantity) FROM Bought)
order by SUM(Quantity) desc
Try this maybe it helps
SELECT Cnum, SUM(Quantity)
FROM Bought
GROUP BY Cnum
HAVING SUM(OrderQuantity) > avg(Quantity)
Try this
SELECT Cnum, Inum, SUM(Quantity) sum, AVG(QUANTITY) average
from bought group by cnum,inum having sum > average