I need to return the count of item sold in last 30 days and
return 0 if there is no item sold. My current query is working well for
getting days where there is item sold but it is not returning 0 values and
I need it.
Here is my current query
SELECT DATE_FORMAT(FROM_UNIXTIME(purchase_date), '%m/%d') AS Dates, COUNT(*) AS COUNT
FROM purchases
WHERE FROM_UNIXTIME(purchase_date) BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
GROUP BY Dates
ORDER BY Dates ASC
The actual result are:
02/15 -> 10
02/16 -> 12
02/18 -> 22
But I'm missing the empty date(s) that I want to return as 0.
This is the table structure:
userid int(11), purchase_item int(11), purchase_date int(11)
You can generate the last 30 days using a recursive CTE:
with recursive dates as (
select curdate() as dte, 1 as n
union all
select dte - interval 1 day, n + 1
from dates
where n < 30
)
The rest is just incorporating this into a query using a LEFT JOIN or correlate subquery:
with recursive dates as (
select curdate() as dte, 1 as n
union all
select dte - interval 1 day, n + 1
from dates
where n < 30
)
select d.dte, count(p.purchase_date)
from dates d left join
purchases p
on from_unixtime(purchase_date) >= d.dte and
from_unixtime(purchase_date) < d.dte + interval 1 day
group by d.dte;
Related
I have a single line in MySQL table: volunteers
user_id | start_date | end_date
11122 | 2017-04-20 | 2018-02-17
How can I find how many times the 3rd day or 24th day of a month appears? (i.e. 2017-05-03, 2017-06-03, 2017-12-24, 2018-01-24) I'm trying to get to the following count:
Sample Output:
user_id | number_of_third_day | number_of_twenty_fourth_day
11122 | 10 | 10
I look at the documentation online to see if there is a way I can say (pseudo):
SELECT
day, COUNT(*)
FROM volunteers
WHERE day(between(start_date, end_date)) in (3,24)
I tried to create a calendar table to no avail, but I would try to get the days, GROUP BY day, and COUNT(*) times that day appears in the range
WITH calendar AS (
SELECT start_date AS date
FROM volunteers
UNION ALL
SELECT DATE_ADD(start_date, INTERVAL 1 DAY) as date
FROM volunteers
WHERE DATE_ADD(start_date, INTERVAL 1 DAY) <= end_date
)
SELECT date FROM calendar;
Thanks for any help!
This one is more optimized since I generate date range by months not days as other questions, so its faster
WITH RECURSIVE cte AS
(
SELECT user_id, DATE_FORMAT(start_date, '%Y-%m-03') as third_day,
DATE_FORMAT(start_date, '%Y-%m-24') as twenty_fourth_day,
start_date, end_date
FROM table1
UNION ALL
SELECT user_id,
DATE_FORMAT(third_day + INTERVAL 1 MONTH, '%Y-%m-03') as third_day,
DATE_FORMAT(twenty_fourth_day + INTERVAL 1 MONTH, '%Y-%m-24') as twenty_fourth_day,
start_date, end_date
FROM cte
WHERE third_day + INTERVAL 1 MONTH <= end_date
)
SELECT user_id,
SUM(CASE WHEN third_day BETWEEN start_date AND end_date THEN 1 ELSE 0 END) AS number_of_third_day,
SUM(CASE WHEN twenty_fourth_day BETWEEN start_date AND end_date THEN 1 ELSE 0 END) AS number_of_twenty_fourth_day
FROM cte
GROUP BY user_id;
Demo here
A dynamic approach is.
but creating the dateranges, takes a lot of time, so you should have a date table to get the dates
CREATE TABLE table1
(`user_id` int, `start_date` varchar(10), `end_date` varchar(10))
;
INSERT INTO table1
(`user_id`, `start_date`, `end_date`)
VALUES
(11122, '2017-04-20', '2018-02-17')
,(11123, '2019-04-20', '2020-02-17')
;
Records: 2 Duplicates: 0 Warnings: 0
WITH RECURSIVE cte AS (
SELECT
user_id,
`start_date` as date_run ,
`end_date`
FROM table1
UNION ALL
SELECT
user_id,
DATE_ADD(cte.date_run, INTERVAL 1 DAY),
end_date
FROM cte
WHERE DATE_ADD(date_run, INTERVAL 1 DAY) <= end_date
)SELECT user_id,
SUM(DAYOFMONTH(date_run) = 3) as day_3th,
SUM(DAYOFMONTH(date_run) = 24) as day_24th
FROM cte
GROUP BY user_id
user_id
day_3th
day_24th
11122
10
10
11123
10
10
fiddle
In last MySQL version you can use recursion:
-- get list of all dates in interval
with recursive dates(d) as (
select '2017-04-20'
union all
select date_add(d, interval 1 day) from dates where d < '2018-02-17'
) select
-- calculate
sum(day(d) = 10) days_10,
sum(day(d) = 24) days_24
from dates
-- filter 10 & 24 days
where day(d) = 10 or day(d) = 24;
https://sqlize.online/sql/mysql80/c00eb7de69d011a85502fa538d64d22c/
As long as you are looking for days that occur in every month (so not the 29th or beyond), this is just straightforward math. The number of whole calendar months between two dates (exclusive) is:
timestampdiff(month,start_date,end_date) - (day(start_date) <= day(end_date))
Then add one if the start month includes the target day and one if the end month includes it:
timestampdiff(month,start_date,end_date) - (day(start_date) <= day(end_date))
+ (day(start_date) <= 3) + (day(end_date) >= 3)
I have the following table called vacations, where the employee id is displayed along with the start and end date of their vacations:
employee
start
end
1001
26/10/21
22/11/21
What I am looking for is to visualize the number of vacation days that each employee had, but separating them by month and without non-working days (Saturdays and Sundays).
For example, if you wanted to view the vacations for employee 1001, the following result should be displayed:
days
month
4
10
16
11
I have the following query that I have worked with:
SELECT id_employee,
EXTRACT(YEAR_MONTH FROM t.Date) as YearMonth,
COUNT(1) as Days
FROM (SELECT v.id_employee,
DATE_ADD(v.start, interval s.seq - 1 DAY) AS Date
FROM vacations v
CROSS JOIN seq_1_to_100 s
WHERE DATE_ADD(v.start, interval s.seq - 1 DAY) <= v.end
ORDER BY v.id_employee, , v.start, s.seq
) t
GROUP BY id_employee,
EXTRACT(YEAR_MONTH FROM t.Date)
With this query I separate the days between a range of two dates with their respective month, but how could I adapt it to stop considering Saturdays and Sundays? I'm working with MySQL 5.7 in phpMyAdmin
instead of count sum the compaarison of weekday function, which give what day it is .
But you should always save fates n a valid mysql manner 2021-10-28
SELECT id_employee,
EXTRACT(YEAR_MONTH FROM t.Date) as YearMonth,
SUM(WEEKDAY(`Date`) < 5) as Days
FROM (SELECT v.id_employee,
DATE_ADD(v.start, interval s.seq - 1 DAY) AS Date
FROM vacations v
CROSS JOIN seq_1_to_100 s
WHERE DATE_ADD(v.start, interval s.seq - 1 DAY) <= v.end
ORDER BY v.id_employee, v.start, s.seq
) t
GROUP BY id_employee,
EXTRACT(YEAR_MONTH FROM t.Date)
I'm trying to get all my result of last 30 days, getting 0 when the count is 0.
My query is:
SELECT substring(mc.publication_date, 1, 10) AS title, count(mc.id) AS quantity
FROM mymyv_cards mc
WHERE mc.publicated = 1 AND STR_TO_DATE(substring(mc.publication_date, 1, 10), "%d-%m-%Y") BETWEEN CURDATE() - INTERVAL 30 DAY AND CURDATE()
GROUP BY substring(mc.publication_date, 1, 10)
And I get this:
But I would like the result was like:
06-04-2021 --> 1
07-04-2021 --> 2
08-04-2021 --> 0
09-04-2021 --> 0
10-04-2021 --> 0
............... etc etc
I don't know how to do that, can you help me?
What you need is a list of all the dates. If you don't have one handy, you can create on using a recursive CTE:
with recursive dates as (
select curdate() as dte, 1 as lev
union all
select dte - interval 1 day, lev + 1
from dates
where lev < 30
)
select *
from dates;
Then you incorporate this into your query with a left join:
with recursive dates as (
select curdate() as dte, 1 as lev
union all
select dte - interval 1 day, lev + 1
from dates
where lev < 30
)
select d.dte, count(mc.id)
from dates d left join
mymyv_cards mc
on mc.publicated = 1 and
str_to_date(left(mc.publication_date, 10), '%d-%m-%Y') >= d.dte and
str_to_date(left(mc.publication_date, 10), '%d-%m-%Y') < d.dte + interval 1 day
group by d.dte;
Note that a column called publication_date should be stored with the value as a date not as a string. You should really fix the data model so the data is stored using the correct types.
Also, you might have a numbers table or calendar table lying around in your database. If so, you can use that instead of the recursive CTE.
I'm writing this query where it gets a row value and it will return the number of records for each day for that row between two given dates and returns 0 if there is no records for that day.
I've written a query which does this for the past week.
Current Query:
select d.day, count(e.event) as count
from (
select 0 day union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6
) d
left join event e
on e.timestamp >= current_date - interval d.day day
and e.timestamp < current_date - interval (d.day - 1) day
and e.event = ?
group by d.day
The problem is this returns only the results for a fixed number of days.. I want to be able to give it two dates (start and end dates) and get the record counts for each day where I don't know the number of dates in between.
You could use/create a bona-fide calendar table. Something like this:
SELECT
d.day,
COUNT(e.timestamp) AS cnt
FROM
(
SELECT '2020-01-01' AS day UNION ALL
SELECT '2020-01-02' UNION ALL
...
SELECT '2020-12-31'
) d
LEFT JOIN event e
ON e.timestamp >= d.day AND e.timestamp < DATE_ADD(d.day, INTERVAL 1 DAY)
WHERE
d.day BETWEEN <start_date> AND <end_date>
GROUP BY
d.day;
I have covered only the calendar year 2020, but you may extend to cover whatever range you want.
This seems like an easy task but my basic sql knowledge is failing me as I'm still learning.
Basically, I'm trying to combine:
SELECT DATE(created) DATE, COUNT(DISTINCT created) newpost FROM surveys
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY GROUP BY DATE(created);
and
SELECT DATE(TIMESTAMP) DATE,subs FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY;
into one query that will return data, grouped by date, into two additional columns - newposts and subs.
I've tried using UNION, which doesn't seem to be giving me the output I want. It combined the data into one column (newpost), and also didn't group by date.
I'm still fairly new to writing MySQL queries, and I've tried searching for answers to no avail. Hoping to seek the knowledge of those smarter than me here.
You could use JOIN
select t1.DATE, t1.newpost, t2.subs
from (
SELECT DATE(created) DATE, COUNT(DISTINCT created) newpost
FROM surveys
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY
GROUP BY DATE(created)
) t1
left join (
SELECT DATE(TIMESTAMP) DATE, subs
FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
) t2 on t1.DATE = t2.DATE
I guess you want one row per distinct date, with two different count values shown.
This kind of query is slightly tricker than it seems at first glance, because the two summary queries might have different sets of dates.
So you need to start with a subquery that yields all possible dates of interest. You then need to LEFT JOIN each summary query to it. You must use LEFT JOIN instead of the ordinary inner JOIN, because LEFT JOIN doesn't suppress rows from the right side of the join when they don't match any rows from the left side.
Here goes:
All your dates. Notice the UNION operation is a setwise (duplicate-removing) union operation.
SELECT DISTINCT DATE(created) DATE FROM newpost
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY
UNION
SELECT DISTINCT DATE(TIMESTAMP) DATE FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
Then you need your two summary subqueries. The first one is this. Notice that I changed COUNT(DISTINCT created) to COUNT(*) because I don't understand the logic behind the DISTINCT there. Can you have more than one row for a single post; do you tell them apart by timestamp? If you have a row for each post you should COUNT(*).
SELECT DATE(created), COUNT(*) newposts
FROM newpost
GROUP BY DATE(created)
The second summary is this. Again, I counted rows.
SELECT DATE(TIMESTAMP), COUNT(*) subs
FROM trafficstats
GROUP BY DATE(TIMESTAMP)
Finally, join those three subqueries like so. You get the dates from the first subquery, and the summary-by-date information from the second two subqueries.
SELECT dates.DATE, posts.newposts, subs.subs
FROM ( /* date subquery */ ) dates
LEFT JOIN ( /* posts subquery */ ) posts ON dates.DATE = posts.DATE
LEFT JOIN ( /* subs subquery */ ) subs ON dates.DATE = subs.DATE
ORDER BY dates.DATE
Putting it all together:
SELECT dates.DATE, posts.newposts, subs.subs
FROM (
SELECT DISTINCT DATE(created) DATE FROM newpost
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY
UNION
SELECT DATE(TIMESTAMP) DATE FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
) dates
LEFT JOIN (
SELECT DATE(created), COUNT(*) newposts
FROM newpost
GROUP BY DATE(created)
) posts ON dates.DATE = posts.DATE
LEFT JOIN (
SELECT DATE(TIMESTAMP), COUNT(*) subs
FROM trafficstats
GROUP BY DATE(TIMESTAMP)
) subs ON dates.DATE = subs.DATE
ORDER BY dates.DATE