I'm using the code below in order to generate data from midnight till now.
SELECT CONCAT(Hour, ':00-', Hour+1, ':00') AS Hours, IFNULL(COUNT(product_id), 0) AS `total_count`
FROM clicks
RIGHT JOIN (
SELECT 0 AS Hour
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
UNION ALL SELECT 22 UNION ALL SELECT 23
) AS AllHours ON HOUR(clicked_at) = Hour
WHERE ( clicked_at BETWEEN CURRENT_DATE() AND NOW() OR clicked_at IS NULL ) AND clicks.site='awesome-site.com'
GROUP BY Hour
ORDER BY Hour
I need the code to return something like
Hours total_count
----------------------
0:00-1:00 19
1:00-2:00 2
2:00-3:00 0
3:00-4:00 0
4:00-5:00 0
5:00-6:00 1
6:00-7:00 0
7:00-8:00 0
8:00-9:00 0
9:00-10:00 4
10:00-11:00 2
11:00-12:00 0
12:00-13:00 17
13:00-14:00 1
The issue is that the query above is return is returning data with gap in the Hours column; something like:
Hours total_count
----------------------
0:00-1:00 19
1:00-2:00 2
5:00-6:00 1
9:00-10:00 4
10:00-11:00 2
12:00-13:00 17
13:00-14:00 1
Thanks for the help.
right join is the correct approach, but you are using columns from clicks table in the where statement. Instead put the filter in on:
SELECT CONCAT(Hour, ':00-', Hour+1, ':00') AS Hours, IFNULL(COUNT(product_id), 0) AS `total_count`
FROM clicks
RIGHT JOIN (
SELECT 0 AS Hour
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
UNION ALL SELECT 22 UNION ALL SELECT 23
) AS AllHours ON HOUR(clicked_at) = Hour
and ( clicked_at BETWEEN CURRENT_DATE() AND NOW() OR clicked_at IS NULL ) AND clicks.site='awesome-site.com'
GROUP BY Hour
ORDER BY Hour
An easy potential solution would be to just have a separate table with all the hours in a day (since the only problem seems to be having 0 entries that fall within certain hours):
Hours
-------------
0:00-1:00
1:00-2:00
2:00-3:00
...
22:00-23:00
23:00-24:00
Then JOIN that to the other table you have, I think basically all the other stuff you do should work with this such as the IFNULL and WHERE ( clicked_at BETWEEN CURRENT_DATE() AND NOW() ...
Related
I have fromDate, toDate and dayOfWeek in mysql. I want all the dates that lies on the particular dayofWeek and lies between fromDate and toDate.
eg
fromDate- '2021-09-01 00:00:00'
toDate- '2021-09-30 00:00:00'
dayOfWeek(4,5) i.e thurday and Friday
output
output
2021-09-02
2021-09-03
2021-09-01
2021-09-10
2021-09-16
2021-09-17
2021-09-23
2021-09-24
2021-09-30
I am using MYSQLWORKBENCH 8.0
here for you
select * from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) as selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where selected_date between '2021-09-01' and '2021-09-30'
having WEEKDAY(selected_date) in (3,4)
ORDER BY selected_date ASC;
0 is monday , 3 is thurday ...
from this post
Another way to get all Thursday and Friday between two dates. I use LAST_DAY() function (last month day) to set the end of the interval, but you can hard code a specific date:
WITH RECURSIVE days AS (SELECT '2021-09-01' as day
UNION ALL
SELECT DATE_ADD(day, INTERVAL 1 DAY)
FROM days
WHERE day < LAST_DAY(day))
SELECT *
FROM days
WHERE weekday(day) IN (3,4);
Output:
day
2021-09-02
2021-09-03
2021-09-09
2021-09-10
2021-09-16
2021-09-17
2021-09-23
2021-09-24
2021-09-30
I have this mysql query that I am trying to translate into laravel query builder can anyone please help?
SELECT CONCAT(HOUR, ':00-', HOUR+1, ':00') AS Hours,
COUNT(o.id) AS id_count
FROM ( SELECT 0 AS HOUR
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
UNION ALL SELECT 22 UNION ALL SELECT 23) AS AllHours
LEFT JOIN orders AS o ON HOUR(created_at) = HOUR AND o.created_at = '2019-10-10'
GROUP BY HOUR
ORDER BY HOUR;
Cheers
Cam
Table Pricing
What i want, is to provide a start_date & an end_date in order to get the price_per_day for each day between those 2 dates.
For example if i set as a start_date = 2015-05-30 & end_date = 2015-06-02, the desired output is
2015-05-30 | 24.00
2015-05-31 | 24.00
2015-06-01 | 27.00
2015-06-02 | 27.00
UPDATE:
Even if this output would be ok for me
24.00
24.00
27.00
27.00
You have to select dates greater than say 'From' date and lesser than 'To' date. I have posted the following without testing so please test and let me know in case of any errors.
SELECT Price FROM Pricing WHERE start_date >= '2015-05-30' AND end_date <= '2015-06-02'
Edit:
Please make sure the start_date and end_date have the same type as the dates provided. Just to be on the safe side, you could also convert them into datetime or convert(varchar, yourDatevariable, 103) but you have to apply it on both sides of the condition.
What you need to do is have a range of dates (ie, 1 row per date). This can be done a few ways. Probably the most efficient is having a calendar table, but if you cannot add new tables that is not possible.
You can have a table of numbers and add that to the starting date of each row, but again this requires a new table.
As such the option is to have a set of unioned queries to generate a range of numbers. For example the following will return 10 rows with the numbers 0 to 9:-
SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
You can cross join such sub queries against each other to generate a larger range of numbers, and add that to your start date where the results is less than of equal to the end date:-
SELECT id, start_date, end_date, price_per_day, DATE_ADD(start_date, INTERVAL (units.a + tens.a * 10 + hundreds.a * 100) DAY) AS aDay
FROM pricing
CROSS JOIN (SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
CROSS JOIN (SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) hundreds
WHERE DATE_ADD(start_date, INTERVAL (units.a + tens.a * 10 + hundreds.a * 100) DAY) <= end_date
The above will cope with up to 1000 days between the dates. Easy to expand to cope with 10000 days or more, but will become slower.
You can then just use that as a sub query when checking the date range you are interested in:-
SELECT aDay, price_per_day
FROM
(
SELECT id, start_date, end_date, price_per_day, DATE_ADD(start_date, INTERVAL (units.a + tens.a * 10 + hundreds.a * 100) DAY) AS aDay
FROM pricing
CROSS JOIN (SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
CROSS JOIN (SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) hundreds
WHERE DATE_ADD(start_date, INTERVAL (units.a + tens.a * 10 + hundreds.a * 100) DAY) <= end_date
) sub0
WHERE aDay BETWEEN '2015-05-30' AND '2015-06-02'
ORDER BY aDay
Your request should look something like this :
SELECT Price FROM Pricing WHERE start_date = 2015-05-30 AND end_date = 2015-06-02
But to print out everydate date between those two, i've no idea.
This might help you thought :
How to list all dates between two dates
Previously I was doing following to get per day count from reports table.
SELECT COUNT(*) AS count_all, tracked_on
FROM `reports`
WHERE (domain_id = 939 AND tracked_on >= '2014-01-01' AND tracked_on <= '2014-12-31')
GROUP BY tracked_on
ORDER BY tracked_on ASC;
Obviously this wont give me 0 count for missing dates.
Then I finally found a optimum solution to generate date-series between given date range.
But the next challenge am facing is to join it with my reports table and get the count grouped by date.
select count(*), all_dates.Date as the_date, domain_id
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) all_dates
inner JOIN reports r
on all_dates.Date >= '2014-01-01'
and all_dates.Date <= '2014-12-31'
where all_dates.Date between '2014-01-01' and '2014-12-31' AND domain_id = 939 GROUP BY the_date order by the_date ASC ;
The result am getting is
count(*) the_date domain_id
46 2014-01-01 939
46 2014-01-02 939
46 2014-01-03 939
46 2014-01-04 939
46 2014-01-05 939
46 2014-01-06 939
46 2014-01-07 939
46 2014-01-08 939
46 2014-01-09 939
46 2014-01-10 939
46 2014-01-11 939
46 2014-01-12 939
46 2014-01-13 939
46 2014-01-14 939
...
Whereas am looking to fill in the missing dates with 0
something like
count(*) the_date domain_id
12 2014-01-01 939
23 2014-01-02 939
46 2014-01-03 939
0 2014-01-04 939
0 2014-01-05 939
99 2014-01-06 939
1 2014-01-07 939
5 2014-01-08 939
...
Another try that I gave was:
select count(*), all_dates.Date as the_date, domain_id
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) all_dates
inner JOIN reports r
on all_dates.Date = r.tracked_on
where all_dates.Date between '2014-01-01' and '2014-12-31' AND domain_id = 939 GROUP BY the_date order by the_date ASC ;
Results:
count(*) the_date domain_id
38 2014-09-03 939
8 2014-09-04 939
Minimal data with above queries: http://sqlfiddle.com/#!2/dee3e/6
You need an OUTER JOIN to arrive at every day between a start and an end because if you use an INNER JOIN it will restrict the output to just the dates that are joined (i.e. just those dates in the report table).
In addition, when you use an OUTER JOIN you must take care that conditions in the where clause don't cause an implicit inner join; for example AND domain_id = 1 if use in the where clause would suppress any row that did not have that condition met, but when used as a join condition it only restricts the rows of the report table.
SELECT
COUNT(r.domain_id)
, all_dates.Date AS the_date
, domain_id
FROM (
SELECT DATE_ADD(curdate(), INTERVAL 2 MONTH) - INTERVAL (a.a + (10 * b.a) ) DAY as Date
FROM (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
CROSS JOIN (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
) all_dates
LEFT OUTER JOIN reports r
ON all_dates.Date = r.tracked_on
AND domain_id = 1
WHERE all_dates.Date BETWEEN '2014-09-01' AND '2014-09-30'
GROUP BY
the_date
ORDER BY
the_date ASC;
I have also changed the all_dates derived table, by using DATE_ADD() to push the starting point into the future, and I have reduced the it's size. Both of these are options and can be tweaked as you see fit.
Demo at SQLfiddle
to arrive at a domain_id for every row (as shown in your question) you would need to use someting like the following; Note you could use IFNULL() which is MySQL specific but I have used COALESCE() which is more generic SQL. However use of an #parameter as shown here is MySQL specific anyway.
SET #domain := 1;
SELECT
COUNT(r.domain_id)
, all_dates.Date AS the_date
, coalesce(domain_id,#domain) AS domain_id
FROM (
SELECT DATE_ADD(curdate(), INTERVAL 2 month) - INTERVAL (a.a + (10 * b.a) ) DAY as Date
FROM (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
CROSS JOIN (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
) all_dates
LEFT JOIN reports r
ON all_dates.Date = r.tracked_on
AND domain_id = #domain
WHERE all_dates.Date BETWEEN '2014-09-01' AND '2014-09-30'
GROUP BY
the_date
ORDER BY
the_date ASC;
See this at SQLfiddle
The all_dates subquery is only looking back from the current day (curdate()). If you want to include future dates, change the first line of the subquery to something like:
select '2015-01-01' - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
I want to get an hourly report of conversions for all 24 hours.
I have this query but it returns only 19rows instead of 24
can anyone plz tell me wats wrong in this?
Thanks in advance.
SELECT HOUR( `date_time` ) AS Hours, COUNT(conversion_id) AS `conversion` FROM conversions
RIGHT JOIN (SELECT 0 AS Hour UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT
5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL
SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23) AS AllHours
ON HOUR(date_time) = Hour
WHERE DATE(date_time) = CURDATE() OR date_time IS NULL
GROUP BY Hour
ORDER BY Hour
If there are not entries for this hour, it is never selected. You have to query the other way round.
I think it should be something like this (hard to test without your database):
select * from (SELECT 0 AS Hour UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23) as AllHours
left outer join
(select COUNT(conversion_id) as cnt, HOUR(date_time) as h
FROM conversions
WHERE DATE(date_time) = CURDATE() OR date_time IS NULL
group by h) as c
on Hour = e.h
The right join is almost correct. I prefer that the where condition be in the on clause (rather than checking for NULL values. The key, though, is using the AllHours table in the select and group by:
SELECT AllHours.Hour AS Hours, COUNT(conversion_id) AS `conversion`
FROM conversions RIGHT JOIN
(SELECT 0 AS Hour UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT
5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL
SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23
) AS AllHours
ON HOUR(conversions.date_time) = AllHours.Hour and DATE(conversions.date_time) = CURDATE()
GROUP BY AllHOurs.Hour
ORDER BY AllHours.Hour