I'm trying to group a table of payments by an entire working day and payment method type (card, cash, stripe..). The thing is (and what I'm not able to accomplish) that a working day does not mean from 00:00 to 00:00 but instead it's a user input value (usually 08:00. I'm forcing 08:00 in the query now). This is because a Bar/Restaurant does not work from 00:00 to 00:00 but usually closes at 3 or 4 am. I also want to order the results by date (which I have already done).
This is the query I've gotten so far:
select * from (select SUM(value) as total, `currency`, `payment_method_type_id`, DATE_FORMAT(created_at, "%Y-%m-%d 08:00:00") as day from payments group by payment_method_type_id, DATE_FORMAT(created_at, "%Y-%m-%d 08:00:00"), currency) as ipd order by day desc
But results are not correct because time is not taken in account when grouping. I want from 08:00 of yesterday to 08:00 of today.
Here's a fiddle with the DB Schema and some inserts.
Thanks a lot,
To solve the problem, subtract 8 yours before changing to a date.
And it can be simplified to
SELECT SUM(value) as total, `currency`, `payment_method_type_id`,
DATE(created_at - INTERVAL 8 HOUR) as day
from payments
group by payment_method_type_id, day, currency
order by day desc
Perhaps you want the ORDER BY list to match the GROUP BY list?
Related
I have a table in which there are 5 columns,
id (auto incrementing number), titleId, version, created_at
A combination of titleId and version is always unique. I want to find out for each day for the past 1 month, how many unique titleIds were present along with the count of how many entries per day. This is because on a given day there might be multiple versions of the same titleId.
select count(*) from titles where created_at >= '2019-08-12 00:00:00' and created_at <= '2019-08-13 00:00:00' will give me total number of titles which came on 12th August
and
select count(distinct titleId) from titles where created_at >= '2019-08-12 00:00:00' and created_at <= '2019-08-13 00:00:00'
will give me the count of unique titles on the 12th August. Is there a way for me to generate the data for the past 30/60 days?
I know I can run this command manually 30 times by changing the date to get the numbers, but was wondering if there is a better way to do this in mysql
As long as there is an entry every day, this query should give you the data for each day for the last 30:
select date(created_at) as cdate, count(distinct titleId) as cnt
from titles
where created_at >= cur_date() - interval 30 day
group by cdate
I'm working to write a MySQL query that outputs the number of new users created by week.
My user table is:
id | created_at
My query:
SELECT YEAR(created_at) AS Year, DATE_FORMAT(created_at, '%b %e') AS Week, COUNT(*) AS total
FROM users
GROUP BY Year, Week;
The problem:
Years: I would like the most recent year to be at the top, currently the oldest year is at the top of the output.
Weeks: The week column is not sorted based on the calendar. For example, the last records shows: 2019 | May 9 | 100
Where I'd like the year and week sorted.
I think you'll find the function YEARWEEK() helpful, not only for the grouping, but also for the ordering.
Also, your use of DATE_FORMAT() doesn't look right, because you're outputing the %e, which is the day of the month, yet you're grouping by week?
SELECT DATE_FORMAT(created_at, '%Y %b %v') AS date, COUNT(*) AS total
FROM users
GROUP BY YEARWEEK(created_at)
ORDER BY YEARWEEK(created_at) DESC;
I'm trying to build a query to find average number of music tracks played per broadcast hour for a given day.
I have a table that logs when a track was played, based on a datetime value (created field).
So I need to count how many entries, or tracks, where logged per hour.
Then with the hourly totals, find the average.
So far I have this, but wondered if it is correct?
SELECT AVG(a.total) FROM (
SELECT HOUR(created) AS hour, COUNT(id) AS total
FROM `music_log` r
WHERE DATE(created) = DATE( DATE_SUB(NOW() , INTERVAL 1 DAY) ) group by HOUR(r.created)
) a
I got to admit, I formulated that from another post on stackoverflow, and don't understand what the a and r mean/reference.
I would like to know if I have this right, so I can expand query to cover a quarter (3 months) results.
You can calculate the average without a subquery:
SELECT COUNT(*) / COUNT(DISTINCT DATE(created), HOUR(created) ) as average
FROM `music_log`
WHERE QUARTER(created) = 1 AND YEAR(created) = YEAR(NOW()) ;
This calculates the total count and the number of hours without the need for a subquery.
As Strawberry says, it looks like I'm on the right track. Thanks Strawberry.
To expand this, and just in case it helps anyone else, I've included the query to cover a quarter, in this case the first quarter of the current year....he says hoping there's nothing else wrong with my query :)
SELECT AVG(a.total) FROM (
SELECT DATE(created) as day, HOUR(created) AS hour, COUNT(id) AS total
FROM `music_log`
WHERE QUARTER(created) = 1
AND YEAR(created) = YEAR(NOW())
group by DATE(created), HOUR(created)
) a
In order to calculate it correctly, I needed to group the hourly results by Date and Hour.
In the previous query in the question, it was only grouping by Hour. Which is fine if the Average calculation is over just a single day, but when you expand that beyond a day, the results become incorrect. This is because it will add the total of both occurrence of 11pm, for example, then work out the average.
Hope that helps...or if I've made a mistake, I hope someone picks up on it ;)
I have table user with column login_time.
I want to select all the users that have logged in more than 10 times in a month.
I tried something like this:
SELECT login_time, count(id) as loginCount FROM user
WHERE login_time between DATE_SUB(login_time INTERVAL 1 month) AND login_time
GROUP BY id, MONTH(login_time) HAVING loginCount > 10;
Im not sure about my selection between dates.
How can I select with a month intervals avoiding double records.
For example if I have this values for login_time:
1. '2015-02-01 14:05:19'
2. '2015-01-21 14:05:19'
3. '2015-01-11 14:05:19'
Both 3 and 2 are within month range of 1.
So will I get double records for that values?
To find the users who have logged in more than ten times in the month ending right now, do this.
SELECT COUNT(*) times_logged_in,
userid
FROM user
WHERE login_time >= NOW() - INTERVAL 1 MONTH
GROUP BY user_id
HAVING COUNT(*)> 10
To find the users who have logged in more than ten times in any calendar month in your table, do this.
SELECT COUNT(*) times_logged_in,
DATE(DATE_FORMAT(login_time, '%Y-%m-01')) month_beginning,
userid
FROM user
GROUP BY user_id, DATE(DATE_FORMAT(login_time, '%Y-%m-01'))
HAVING COUNT(*)> 10
The trick here is the expression DATE(DATE_FORMAT(login_time, '%Y-%m-01')), which converts any timestamp to the first day of the month in which it occurs.
Your question mentioned this WHERE condition:
WHERE login_time between DATE_SUB(login_time INTERVAL 1 month) AND login_time
This doesn't do anything interesting because it always comes back true. Each given login_time always falls in the interval you specified.
Edit: You can GROUP BY MONTH(dt) if you want. But the way I have shown it automatically accounts for years as well as months, so in my opinion it's much better for accurate reporting.
Another edit: This formula yields the preceding Sunday for any given date or timestamp item.
FROM_DAYS(TO_DAYS(login_time) -MOD(TO_DAYS(login_time) -1, 7))
If Monday is the first day of the week in your jurisdiction, change the -1 to -2. Grouping by this function is superior to doing GROUP BY WEEK(login_time) because WEEK() does odd things at the beginnings and ends of calendar years.
This is all written up in more detail here: http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
I've been Googling for a few hours... thought this would be easy, but clearly not for me :)
I've got sales data in two tables and I want to generate a weekly sales report for a specific item. For this purpose, I don't care about dollar values, just number of units. An a "week" is either a calendar week (whatever start day, I don't care) or just 7-day chunks back from current (so week 1 is the last 7 days, week 2 is 8 - 15 days ago, etc) - whichever is easier. I'm simply trying to monitor sales trends over time. Preferably it would span back over years so that if its the first week of January, for example, it wouldn't show just one record.
The data comes from ZenCart. The relevant tables/column structure is here:
Table "orders" has columns: orders_id, date_purchased
Table "orders_products" has columns: orders_id, products_id, product_quantity
Where I'm having trouble is with the joins and syntax.
This worked for my needs:
SELECT o.date_purchased, CONCAT(YEAR(o.date_purchased), LPAD(WEEK(o.date_purchased), 2, '0')) as weekyear, op.products_id, SUM( op.products_quantity )
FROM orders_products op
LEFT JOIN orders o ON op.orders_id = o.orders_id
WHERE op.products_id = 331
GROUP BY weekyear
ORDER BY weekyear
If you have some date/datetime/timestamp column, you can use the week function in your where clause
select week(now()) as week, sum(units) as total
from sales
where week(sales_date) = week(now());
or the previous week
select week(now()) - 1 as week, sum(units) as total
from sales
where week(sales_date) = week(now()) - 1;
You must take care for the year wrap around from week 52/53 to week 0/1.
SQLFiddle for testing.
In order to take care of the year end wrap. for instance, week(12/30/2018)=52 and week(12/31/2018)=52 both are considered week 52 of 2018. the first day of 2019 starts on a Tuesday. you can write a case statement as follows to move 12/30/2018 and 12/31/2018 to the first week of 2019. so that you will have a complete 7 days week to compare:
case when order_date in ( '2018-12-30', '2018-12-31')
then 0
else week(order_date)
end as order_week