I need to get results month by month for the last 3 years. This is what I have:
CREATE DEFINER=`root`#`localhost` PROCEDURE `by_year`()
BEGIN
DROP TEMPORARY TABLE IF EXISTS aaa;
SET #year = 2014;
WHILE #year <= YEAR(CURDATE()) DO
CREATE TEMPORARY TABLE IF NOT EXISTS aaa (Año int, Registrados int, Activos int, Compraron int, Recurrentes int);
INSERT INTO aaa
SELECT #year, registrados.count, activos.count, compraron.count, recurrentes.count FROM
# Here is my query to get the data. With a WHERE that limits the creation date by #year. [YEAR(AU.created) <= #year] For example
END WHILE;
SELECT * FROM aaa;
END
It works for years. But I need to get the result by months or even weeks and days.
My problem is if I replace the YEAR function with MONTH or DAY These functions will return a number taken from the date literally (relative to the bigger divider)
For example from
MONTH('2015-11-25')
I get
11
So MONTH('2015-11-25') is equal to MONTH('2016-11-02')
I see two ways... Either I find a function or something like:
MONTH_FROM_THE_BEGGINING_OF_TIMES('2016-11-02')
That similarly to dates handled by TIME, returns the month number in general in this case from year 1 that should be somenthing like (2015 * 12) + 11 = 24191
The other way could be playing around with double WHILE. And then for days triple WHILE. This will produce a complexity of O(n^3) which is not good.
Any ideas are very welcome.
Thank you
You might like MySQL's EXTRACT() function:
mysql> SELECT EXTRACT(YEAR_MONTH FROM CURDATE()) AS YM;
+--------+
| YM |
+--------+
| 201608 |
+--------+
If you want days, you might like TO_DAYS():
mysql> SELECT TO_DAYS(CURDATE()) AS days;
+--------+
| days |
+--------+
| 736564 |
+--------+
You cant not replace the function YEAR, you just need to add the function MONTH to get the result by months with the logical AND operator. Also in your query to get the data you have to add the AND part for the month at the group by.
It is the only way to get the month number relative to the year.
Related
How do I split a selected date value into many rows, with as a known interval until now for example? I could do it using Recursive CTE, but MySql Aurora(RDS) does't give support.
eg: taking a single result like
|--|----------|
|id|createdAt |
|--|----------|
|1 |2018-01-01|
|--|----------|
Given a 7 days interval, the query should output data like:
|----------|
|clycles |
|----------|
|2018-01-01|
|2018-01-08|
|2018-01-15|
|2018-01-22|
|... |
|----------|
I would use temporary table for this
DELIMITER //
CREATE PROCEDURE `increment_date_till_now`(
IN `date_start` DATE,
IN `day_interval` INT
)
BEGIN
CREATE TEMPORARY TABLE `__increment_days` (`date` DATE NOT NULL);
SET #dt = date_start;
INSERT INTO `__increment_days` VALUES (#dt);
WHILE (#dt := #dt + INTERVAL day_interval DAY) < CURRENT_DATE() DO
INSERT INTO `__increment_days` VALUES (#dt);
END WHILE;
SELECT * FROM `__increment_days`;
DROP TABLE `__increment_days`;
END//
And then
CALL increment_date_till_now('2018-01-01', 7);
If you have a (possibly temporary) table of all possible dates in a column d, you can
SELECT whatever, d FROM somewhere
JOIN dates
WHERE d BETWEEN createdAt AND CURRENT_DATE()
AND MOD(DATEDIFF(d, createdAt),7)=0
See also How to populate a table with a range of dates?
I have a mysql table with date column. date column data type is TIMESTAMP and default set to CURRENT_TIMESTAMP which record both date and time
now i want to count my rows under given day
As an example
++++++++++ 8am 9am 10am 11am
++user1+++ 15 10 11 10
++user2+++ 10 10 20 30
Every hour count should be recorded separately like this.
i tried with this but it's not working
SELECT COUNT(*) FROM mytable
WHERE `date` = '2015-01-26'
GROUP BY HOUR(`TIMESTAMP`)
how can i achieve this ?
i have no idea how to group with user . sproc is also okay
I made a sproc like this. but this sproc contain errors. can some one please help me now i want to count this separated by 9 hours
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `test22`(IN datestamp DATE)
BEGIN
SELECT username,
COUNT(if(disblid,1,null)) '8:00 AM', where time between '08:00' and '09:00':
COUNT(if(disblid,1,null)) '9:00 AM' , where time between '09:00' and '10:00';
FROM claimloans
WHERE DATE(date) = datestamp
group by Username;
END
Thanks for everyone who helped me I come up with sproc that working perfectly fine.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `claimscounter`(IN datestamp DATE)
BEGIN
SELECT username,
COUNT(IF(HOUR(date)=0,1,NULL)) AS '12am',
COUNT(IF(HOUR(date)=1,1,NULL)) AS '1am',
COUNT(IF(HOUR(date)=2,1,NULL)) AS '2am',
COUNT(IF(HOUR(date)=3,1,NULL)) AS '3am',
COUNT(IF(HOUR(date)=4,1,NULL)) AS '4am',
COUNT(IF(HOUR(date)=5,1,NULL)) AS '5am',
COUNT(IF(HOUR(date)=6,1,NULL)) AS '6am',
COUNT(IF(HOUR(date)=7,1,NULL)) AS '7am',
COUNT(IF(HOUR(date)=8,1,NULL)) AS '8am',
COUNT(IF(HOUR(date)=9,1,NULL)) AS '9am',
COUNT(IF(HOUR(date)=10,1,NULL)) AS '10am',
COUNT(IF(HOUR(date)=11,1,NULL)) AS '11am',
COUNT(IF(HOUR(date)=12,1,NULL)) AS '12pm',
COUNT(IF(HOUR(date)=13,1,NULL)) AS '1pm',
COUNT(IF(HOUR(date)=14,1,NULL)) AS '2pm',
COUNT(IF(HOUR(date)=15,1,NULL)) AS '3pm',
COUNT(IF(HOUR(date)=16,1,NULL)) AS '4pm',
COUNT(IF(HOUR(date)=17,1,NULL)) AS '5pm',
COUNT(IF(HOUR(date)=18,1,NULL)) AS '6pm',
COUNT(IF(HOUR(date)=19,1,NULL)) AS '7pm',
COUNT(IF(HOUR(date)=20,1,NULL)) AS '8pm',
COUNT(IF(HOUR(date)=21,1,NULL)) AS '9pm',
COUNT(IF(HOUR(date)=22,1,NULL)) AS '10pm',
COUNT(IF(HOUR(date)=23,1,NULL)) AS '11pm'
FROM claimloans
WHERE DATE(date) = datestamp
group by username;
END
But now I have another small problem. This count all the hours. if it's not entry for some hour it count as zero. I want to count hours only have records can someone help me with this
thnaks
I would solve this as a view, like so:
CREATE TABLE foo (id int not null, val timestamp);
CREATE VIEW foo_by_hours AS (
SELECT
id,
DATE(val) AS 'day',
COUNT(IF(HOUR(val)=0,1,NULL)) AS '12am',
COUNT(IF(HOUR(val)=1,1,NULL)) AS '1am',
COUNT(IF(HOUR(val)=2,1,NULL)) AS '2am',
COUNT(IF(HOUR(val)=3,1,NULL)) AS '3am',
...
FROM foo
GROUP BY id, day);
SELECT * FROM foo_by_hours;
Full example on SQL Fiddle
I also added a view which uses SUM instead of COUNT. The result is the same, it's just a different way of doing it.
I have a table with an ID, a datetime that something occurred, and a description of what it was.
With that table, I've been able to query for the number of times that something occurs during that day of the week with the following:
mysql> select distinct(DATE(HEADER_DATE)) from emails_inbound where WEEKDAY(HEADER_DATE) = 6;
+---------------------+
| (DATE(HEADER_DATE)) |
+---------------------+
| 2014-09-21 |
| 2014-09-28 |
| 2014-10-05 |
+---------------------+
3 rows in set (0.00 sec)
What I'd like to see is a count of the number of days of the week (Sundays, in this example) that nothing occured (no result returned for that day) starting since a particular day.
In other words, how many Sundays since 9/1/2014 contain zero results?
Many thanks in advance!
Just put together a temp table that will give you a row for each day in your desired date range, and then LEFT JOIN that against your main table and GROUP BY the weekday.
I do not have MySQL at my disposal and this seems to be difficult to do in SQLFiddle (mix schema & non-schema statements), but the following code should work as-is or with very minor tweaks. It is important to note that I assume that the ID column of your emails_inbound table is called id; if it is something else, change e.id to e.whatever.
drop temp table if exists t_tmp;
create temp table t_tmp (
my_date date
) engine=memory;
declare v_startDate date;
declare v_endDate date;
set v_startDate = '10-1-2014'; -- YOUR START DATE
set v_endDate = '10-10-2014'; -- YOUR END DATE
while (v_startDate < v_endDate) do
insert into t_tmp values (v_startDate);
set v_startDate = v_startDate + interval 1 day;
end while;
select weekday(date(e.header_date)) as day_of_week,
count(e.id) as number_of_days_when_this_event_happened,
sum(case when e.id is not null then 0 else 1 end) as number_of_days_when_this_event_did_not_happen
from t_tmp x
left join emails_inbound e on date(e.header_date) = x.my_date
group by weekday(date(e.header_date))
drop temp table t_tmp;
I am working with MySQL database and I have to generate date intervals for specified period(specified by start and stop date) with specified step(for example one day).
I have written a stored procedure to generate intervals, to create a temporary table and to populate this table with intervals.
DELIMITER $$
CREATE PROCEDURE showu(IN start date, IN stop date)
BEGIN
CREATE TEMPORARY TABLE intervals(single_day DATE);
next_date: LOOP
IF start>stop THEN
LEAVE next_date;
END IF;
INSERT INTO intervals(single_day) VALUES(start);
SET start = DATE_ADD(start, INTERVAL 1 DAY);
END LOOP next_date;
END$$
DELIMITER ;
I want to use this temporary table in join queries. However I faced with a problem. When I call procedure call showu('2008-01-09', '2010-02-09'); it is executing approximately 30 seconds. The question why it is executing so long? Is it possible to improve it? If this solution is wrong how can I resolve my problem in different way?
From comments:
2 big problems: 1. I don't know exactly value of step(one day or one month or one hour).
Create one big table like this once (not temporary):
full_date | year | month | day | full_time | hour | minute | is_weekend | whatever
----------------------------------------------------------------------------------
...
Create as much indexes as needed and you will have a very performant a powerful swiss knife for all sorts of reports.
Note: You might consider not having time and date in the same table. This is just to simplify the example.
Your second problem
I will clog my database with not model data.
is no problem. Databases are there to hold data. That's it. If you have problems with space or whatever, the solution is to get more space, not to limit your ability to work efficiently.
That being said, here's some examples how to use this table.
You need dates:
SELECT
*
FROM
(
SELECT full_date AS your_step
FROM your_new_swiss_army_knife
WHERE `year` = 2012
GROUP BY full_date
) dates
LEFT JOIN your_tables_that_you_want_to_build_a_report_on y ON dates.your_step = y.date
Same with months:
SELECT
*
FROM
(
SELECT CONCAT(year, '-', month) AS your_step
FROM your_new_swiss_army_knife
WHERE full_date BETWEEN this AND that
GROUP BY year, month
) dates
LEFT JOIN your_tables_that_you_want_to_build_a_report_on y ON dates.your_step = CONCAT(YEAR(y.date), '-', MONTH(y.date))
I have a log table with a date field called logTime. I need to show the number of rows within a date range and the number of records per day. The issue is that i still want to show days that do not have records.
Is it possible to do this only with SQL?
Example:
SELECT logTime, COUNT(*) FROM logs WHERE logTime >= '2011-02-01' AND logTime <= '2011-02-04' GROUP BY DATE(logTime);
It returns something like this:
+---------------------+----------+
| logTime | COUNT(*) |
+---------------------+----------+
| 2011-02-01 | 2 |
| 2011-02-02 | 1 |
| 2011-02-04 | 5 |
+---------------------+----------+
3 rows in set (0,00 sec)
I would like to show the day 2011-02-03 too.
MySQL will not invent rows for you, so if the data is not there, they will naturally not be shown.
You can create a calendar table, and join in that,
create table calendar (
day date primary key,
);
Fill this table with dates (easy with a stored procedure, or just some general scripting), up till around 2038 and something else will likely break unitl that becomes a problem.
Your query then becomes e.g.
SELECT logTime, COUNT(*)
FROM calendar cal left join logs l on cal.day = l.logTime
WHERE day >= '2011-02-01' AND day <= '2011-02-04' GROUP BY day;
Now, you could extend the calendar table with other columns that tells you the month,year, week etc. so you can easily produce statistics for other time units. (and purists might argue the calendar table would have an id integer primary key that the logs table references instead of a date)
In order to accomplish this, you need to have a table (or derived table) which contains the dates that you can then join from, using a LEFT JOIN.
SQL operates on the concept of mathematical sets, and if you don't have a set of data, there is nothing to SELECT.
If you want more details, please comment accordingly.
I'm not sure if this is a problem that should be solved by SQL. As others have shown, this requires maintaining a second table that contains the all of the individual dates of a given time span, which must be updated every time that time span grows (which presumably is "always" if that time span is the current time.
Instead, you should use to inspect the results of the query and inject dates as necessary. It's completely dynamic and requires no intermediate table. Since you specified no language, here's pseudo code:
EXECUTE QUERY `SELECT logTime, COUNT(*) FROM logs WHERE logTime >= '2011-02-01' AND logTime <= '2011-02-04' GROUP BY DATE(logTime);`
FOREACH row IN query result
WHILE (date in next row) - (date in this row) > 1 day THEN
CREATE new row with date = `date in this row + 1 day`, count = `0`
INSERT new row IN query result AFTER this row
ADVANCE LOOP INDEX TO new row (`this row` is now the `new row`)
END WHILE
END FOREACH
Or something like that
DECLARE #TOTALCount INT
DECLARE #FromDate DateTime = GetDate() - 5
DECLARE #ToDate DateTime = GetDate()
SET #FromDate = DATEADD(DAY,-1,#FromDate)
Select #TOTALCount= DATEDIFF(DD,#FromDate,#ToDate);
WITH d AS
(
SELECT top (#TOTALCount) AllDays = DATEADD(DAY, ROW_NUMBER()
OVER (ORDER BY object_id), REPLACE(#FromDate,'-',''))
FROM sys.all_objects
)
SELECT AllDays From d