Creating a view in MySQL that needs to update itself weekly - mysql

I'm trying to create views in MySQL Workbench which would serve me to extract weekly reports, that covers the period between Monday and Sunday.
I don't know how to write it dynamically so that the date gets updated by itself.
This is the query i wrote:
SELECT from_unixtime(date, '%d/%m/%Y'), count(distinct(playerId)) FROM innodb.player_spin
WHERE date between 1512950400 and 1513555199
GROUP BY from_unixtime(date, '%d/%m/%Y');

I usually work in T-SQL, so I cannot confirm that this works by running it.
Use the Weekday function and some date math
SELECT from_unixtime(date, '%d/%m/%Y'), count(distinct(playerId)) FROM
innodb.player_spin
where date between (Date_ADD(CURDATE(), INTERVAL (0 - WEEKDAY(CURDATE()) DAY) and (Date_ADD(CURDATE(), INTERVAL (0 - WEEKDAY(CURDATE() +7) DAY)
group by from_unixtime(date, '%d/%m/%Y');

Related

Create a dynamic time range in sql

Is there a way to create a dynamic way to make a sql query that queries the data between the first day and the last day of every current month? I have a field called created and and I want to get the data where created is between the first day and the last day of the month.
Something like this will do it:
WHERE created BETWEEN DATE_FORMAT(Now(), '%Y-%M-01 00:00:00') AND DATE_FORMAT(DATE_ADD(DATE_ADD(Now(),INTERVAL 1 MONTH), INTERVAL -12 HOUR), '%Y-%M-%d 23:59:59')
It’s not pretty, but it gets the job done.
Is there a way to create a dynamic way to make a sql query that queries the data between the first day and the last day of every current month?
A simple method uses just date functions:
where created_at >= curdate() + interval (1 - day(curdate()) day and
created_at < (curdate() + interval (1 - day(curdate()) day) + interval 1 month
In addition to only using date arithmetic, this doesn't have an error of missing the last second of the month.

mysql BETWEEN date range not working

I have a table called barcode_log, and these are all the datas from the table.
And now if I run this query
SELECT * FROM `barcode_log` WHERE barcode_log.assign_time BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) AND CURRENT_DATE;
I get this result
But it should return all the rows as all the data is within this month only. And assign_time field is stored as datetime. Any idea what i am doing wrong??
You are ignoring the time part (hh:mm:ss).
If the end day is set to the end timestamp of the current date then you can get the data of current day's too.
BETWEEN is inclusive
SELECT
*
FROM
`barcode_log`
WHERE
barcode_log.assign_time BETWEEN DATE_SUB(
CURRENT_DATE,
INTERVAL 30 DAY
)
AND TIMESTAMP(CONCAT(CURDATE(),' ','23:59:59'));
While the accepted answer works, there is a simpler solution. Just take the date part of the datetime column:
SELECT
*
FROM
`barcode_log`
WHERE
DATE(barcode_log.assign_time)
BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) AND CURRENT_DATE;
There is another way around: CAST() on barcode_log.assign_time field.
SELECT *
FROM `barcode_log`
WHERE CAST(barcode_log.assign_time AS DATE)
BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) AND CURRENT_DATE;
This excludes time from comparison and works fine for your purpose.

Select all MySQL records in the last day that are between a time range of 7am and midnight

I have this query
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
which can get record in the last day but I need to limit to records created after 7AM
Any help please?
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
and hour(`clock_in_datetime`) > 7;
Added one more filter condition to check for the hour.
Your query was almost correct, because CURDATE() only gives the date you can just subtract 17 hours to get the correct result. fiddle.
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` >= DATE_SUB(CURDATE(), INTERVAL 17 HOUR)
To get the entries of the current day, we can add 7 hours (CURDATE() has time 0:00).
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` >= DATE_ADD(CURDATE(), INTERVAL 7 HOUR)
To get only rows from yesterday, with a time value of 7AM or later, we can add 7 hours to the expression.
If we only up until midnight of today (just rows from yesterday), we can add another condition, the datetime is less than midnight today.
For example:
SELECT t.*
FROM `timeclock_timecard` t
WHERE t.`clock_in_datetime` >= DATE(NOW()) + INTERVAL -1 DAY + INTERVAL 7 HOUR
AND t.`clock_in_datetime` < DATE(NOW())
If you want to exclude the exact 7:00:00 AM value, change the >= to just >.
FOLLOWUP
Q: What I actually want is between about 5-6am TODAY and mindnight TODAY so anytime during today that I run the report for today I will get only timeclock data from users who clocked in/out today only and not include yesterdays data.
A: The predicates are going to be of the form
WHERE t.`clock_in_datetime` >= expr1
AND t.`clock_in_datetime` < expr2
You just need to find the expressions expr1 and expr2 that return the appropriate datetime values.
Just use a simple SELECT statement to test:
SELECT DATE(NOW()) + INTERVAL 5 HOUR AS `start`
, DATE(NOW()) + INTERVAL 1 DAY AS `end`
Q: I also modified my select to take in account my datetime is in UTC and my result needs to get todays records using local timezone.
SELECT * , CONVERT_TZ( clock_in_datetime , '+00:00', '-4:00' ) FROM `timeclock_timecard`
A: Personally, I would do the timezone conversion on the exprN values, not the column values. Having predicates on bare columns allows MySQL to make effective use of an index; wrapping the columns in expressions prevents MySQL from using an index.
If the MySQL system clock is UTC, and your datetime values stored in the table are in a different timezone, yes, use the MySQL CONVERT_TZ function.
Again, using a simple SELECT statement to develop and test the expressions:
SELECT CONVERT_TZ( DATE(NOW()) + INTERVAL 5 HOUR, '+0:00', to_tz) AS `start`
, CONVERT_TZ( DATE(NOW()) + INTERVAL 1 DAY , '+0:00', to_tz) AS `end`
Where to_tz is the timezone of the values in the table.
Once you get expressions start and end returning the values you need, then use those expressions in the predicates of the query of the timecard table.

date condition to retrieve data in a 24 hour time window mysql

I am using perl and DBI to query a mysql table. I need to retrieve all rows (aprox. 75,000 rows from 3 separate databases) within the past 24 hours, ideally between 12:00 am and 11:59 pm or 00:00:00 and 23:59:59.
I was using a WHERE date condition like this:
SELECT *
FROM table_name
WHERE insert_date >= DATE_SUB(NOW(), INTERVAL 1 DAY);
Then I would run my script at midnight using cron. This worked well enough, but due to a regular large volume of traffic at midnight and the size of the queries, the execution time scheduled with cron is now 3:00 am. I changed my sql to try and get the same 24 hour period from an offset like this:
SELECT *
FROM table_name
WHERE insert_date
BETWEEN DATE_SUB(DATE_SUB(NOW(), INTERVAL 3 HOUR), INTERVAL 1 DAY)
AND DATE_SUB(NOW(), INTERVAL 3 HOUR);
This seems to work fine for my purposes but I want to ask, is there is a more readable and more accurate way, using mysql, to get all rows from the past 24 hours ( between 00:00:00 and 23:59:59 time window ) once a day while running the query from an offset time? I am generally new to all of this so any critiques on my overall approach are more than welcome.
I presume insert_date is a DATETIME?
It seems pointless to go to all the trouble of building two limits and using BETWEEN. I would simply check that DATE(insert_date) is yesterday's date. So
WHERE DATE(insert_date) = CURDATE() - INTERVAL 1 DAY
BETWEEN DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), "%Y-%M-%d 00:00:00")
AND DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), "%Y-%M-%d 23:59:59")
You could also use Perl date formatting functions to produce the same date-time strings, and interpolate them into the query.
....
WHERE insert_date BETWEEN CURRENT_DATE - INTERVAL 1 DAY
AND
CURRENT_DATE - INTERVAL 1 SECOND;
The lower bound will be coerced to yesterday's YYYY-MM-DD 00:00:00, and this WHERE predicate will be able to make use of an index on insert_date.
Considering that DATE(NOW()) implicitly means midnight this morning, the obvious solution is to take that value and subtract a day for the start... and subtract a second for the end.
BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 1 DAY)
AND DATE_SUB(DATE(NOW()), INTERVAL 1 SECOND)

How to get first day of every corresponding month in mysql?

I want to get first day of every corresponding month of current year. For example, if user selects '2010-06-15', query demands to run from '2010-06-01' instead of '2010-06-15'.
Please help me how to calculate first day from selected date. Currently, I am trying to get desirable using following mysql select query:
Select
DAYOFMONTH(hrm_attendanceregister.Date) >=
DAYOFMONTH(
DATE_SUB('2010-07-17', INTERVAL - DAYOFMONTH('2010-07-17') + 1 DAY
)
FROM
hrm_attendanceregister;
Thanks
Is this what you are looking for:
select CAST(DATE_FORMAT(NOW() ,'%Y-%m-01') as DATE);
You can use the LAST_DAY function provided by MySQL to retrieve the last day of any month, that's easy:
SELECT LAST_DAY('2010-06-15');
Will return:
2010-06-30
Unfortunately, MySQL does not provide any FIRST_DAY function to retrieve the first day of a month (not sure why). But given the last day, you can add a day and subtract a month to get the first day. Thus you can define a custom function:
DELIMITER ;;
CREATE FUNCTION FIRST_DAY(day DATE)
RETURNS DATE DETERMINISTIC
BEGIN
RETURN ADDDATE(LAST_DAY(SUBDATE(day, INTERVAL 1 MONTH)), 1);
END;;
DELIMITER ;
That way:
SELECT FIRST_DAY('2010-06-15');
Will return:
2010-06-01
There is actually a straightforward solution since the first day of the month is simply today - (day_of_month_in_today - 1):
select now() - interval (day(now())-1) day
Contrast that with the other methods which are extremely roundabout and indirect.
Also, since we are not interested in the time component, curdate() is a better (and faster) function than now(). We can also take advantage of subdate()'s 2-arity overload since that is more performant than using interval. So a better solution is:
select subdate(curdate(), (day(curdate())-1))
This is old but this might be helpful for new human web crawlers XD
For the first day of the current month you can use:
SELECT LAST_DAY(NOW() - INTERVAL 1 MONTH) + INTERVAL 1 DAY;
You can use EXTRACT to get the date parts you want:
EXTRACT( YEAR_MONTH FROM DATE('2011-09-28') )
-- 201109
This works well for grouping.
You can use DATE_FORMAT() function in order to get the first day of any date field.
SELECT DATE_FORMAT(CURDATE(),'%Y-%m-01') as FIRST_DAY_CURRENT_MONTH
FROM dual;
Change Curdate() with any other Date field like:
SELECT DATE_FORMAT(purchase_date,'%Y-%m-01') AS FIRST_DAY_SALES_MONTH
FROM Company.Sales;
Then, using your own question:
SELECT *
FROM
hrm_attendanceregister
WHERE
hrm_attendanceregister.Date) >=
DATE_FORMAT(CURDATE(),'%Y-%m-01')
You can change CURDATE() with any other given date.
There are many ways to calculate the first day of a month, and the following are the performance in my computer (you may try this on your own computer)
And the winner is LAST_DAY(#D - interval 1 month) + interval 1 day
set #D=curdate();
select BENCHMARK(100000000, subdate(#D, (day(#D)-1))); -- 33 seconds
SELECT BENCHMARK(100000000, #D - INTERVAL (day(#D) - 1) DAY); -- 33 seconds
SELECT BENCHMARK(100000000, cast(DATE_FORMAT(#D, '%Y-%m-01') as date)); -- 29 seconds
SELECT BENCHMARK(100000000, LAST_DAY(#D - interval 1 month) + interval 1 day); -- 26 seconds
I'm surprised no one has proposed something akin to this (I do not know how performant it is):
CONCAT_WS('-', YEAR(CURDATE()), MONTH(CURDATE()), '1')
Additional date operations could be performed to remove formatting, if necessary
use date_format method and check just month & year
select * from table_name where date_format(date_column, "%Y-%m")="2010-06"
SELECT LAST_DAY(date) as last_date, DATE_FORMAT(date,'%Y-%m-01') AS fisrt_date FROM table_name
date=your column name
The solutions that use last_day() and then add/subtract a month and a day are not interchangeable.
Example:
date_sub(date_add(last_day(curdate()), interval 1 day), interval 3 month)
always works for any supplied number of months you want to go back
date_add(date_sub(last_day(now()), interval 3 month), interval 1 day)
will fail in some cases, for instance if your current month has 30 days and the month you're subtracting back to (and then adding a day) has 31.
date_add(subdate(curdate(), interval day(?) day), interval 1 day)
change the ? for the corresponding date
This works fine for me.
date(SUBDATE("Added Time", INTERVAL (day("Added Time") -1) day))
** replace "Added Time" with column name
Use Cases:
If you want to reset all date fields except Month and Year.
If you want to retain the column format as "date". (not as "text" or "number")
Slow (17s):
SELECT BENCHMARK(100000000, current_date - INTERVAL (day(current_date) - 1) DAY);
SELECT BENCHMARK(100000000, cast(DATE_FORMAT(current_date, '%Y-%m-01') as date));
If you don't need a date type this is faster:
Fast (6s):
SELECT BENCHMARK(100000000, DATE_FORMAT(CURDATE(), '%Y-%m-01'));
SELECT BENCHMARK(100000000, DATE_FORMAT(current_date, '%Y-%m-01'));
select big.* from
(select #date := '2010-06-15')var
straight_join
(select * from your_table where date_column >= concat(year(#date),'-',month(#date),'-01'))big;
This will not create a full table scan.