> Start stop pinnumber
> ---------------------------------------------------------
> 2012-03-14 13:22:17 2012-03-14 15:22:50 2001
> 2012-03-14 18:11:10 2012-03-14 19:10:10 2001
> 2012-03-15 07:20:10 2012-03-15 13:20:50 2001
>**2012-03-16 19:21:55 2012-03-17 02:55:22 2001** //on 16(19:21:55
to 23:59:59) and
//on 17(00 to 02:55:22)
> 2012-03-17 14:15:05 2012-03-17 17:44:50 2001
> 2012-03-18 19:11:10 2012-03-18 19:10:10 2002
> 2012-03-18 10:20:10 2012-03-18 13:20:50 2003
> 2012-03-18 11:20:10 2012-03-18 15:11:50 2001
Question:
How can I calculate total time of each user of per day ('start', 'stop') per day? Please see the above highlighted point. Suppose, If user 'start' today and stop it tomorrow then today hour are different and tomorrow hour are different?
right now i am using following query:-
SELECT SEC_TO_TIME( SUM( TIME_TO_SEC(TIMEDIFF(stop ,start ) ) ) ) AS time1, clock. * FROM table_name WHERE pin_number = '2001' GROUP BY DATE_FORMAT( start , '%W %M %Y' )
from above query i am getting per day records but when start date and stop date is different. it calculate total time not single day time but i need per day time.
I think I finally got there. You first need to get a set of days, which I obtain through a subquery that takes a UNION of the start and stop times (you could filter this for your pinnumber if desired in order to reduce the size of the JOIN).
One then joins each such date with those (start,stop) pairs that encompass that date (i.e. either start during the day, or the start of the day is between the start and stop time).
Finally, one groups by day and takes the sum of the amount of time between the start and end times, cutting off at the day start and end as appropriate (the magic 86400 is the number of seconds in a day = 24*60*60). Sadly this won't play nice with daylight savings, leap seconds, etc...
SELECT FROM_UNIXTIME(unixday, '%d/%m/%Y'), SUM(
LEAST( unixday+86400, UNIX_TIMESTAMP(Stop ))
- GREATEST(unixday , UNIX_TIMESTAMP(Start))
) AS Seconds
FROM table_name JOIN (
SELECT UNIX_TIMESTAMP(DATE(Start)) AS unixday FROM table_name
UNION
SELECT UNIX_TIMESTAMP(DATE(Stop )) AS unixday FROM table_name
) AS days ON (
UNIX_TIMESTAMP(Start) BETWEEN unixday AND unixday+86400
OR (unixday BETWEEN UNIX_TIMESTAMP(Start) AND UNIX_TIMESTAMP(Stop))
)
WHERE pinnumber = 2001
GROUP BY unixday;
See it on sqlfiddle.
Related
I Have a table booking which has two columns job_time_beg and job_time_end and jobs starts from 8:00 and goes till 16:00 Now I need to if the available time overlap with the given time so that I know I dont want to book within this given time.
I am using
select count(*)as cnt from orders
where job_date = '2021-02-04'
and job_truck = '24'
and ((job_time_beg >= '10:00' and job_time_beg < '12:00')
or (job_time_end > '(11):00' and job_time_end <= '12:00'))
AND order_status_id in (1,5)
this works fine for most cases but if there is a booking between 8:00 and 11:00 this query does not work and shows that the time 10:00 to 12:00 is available which overlap between 8:00 and 11:00
I also tried
SELECT count(*) as cnt
FROM orders
WHERE
job_date = '$data'
and job_truck = '$truck' and
(
'$start' BETWEEN job_time_beg and job_time_end
OR
'$end' BETWEEN job_time_beg and job_time_end
)
AND order_status_id in (1,5)
which works for the 8:00 and 11:00 overlap but does not work when the booking is available for ex start time can be equal to end time in the next booking if we have booking 8:00 to 10:00 and we get time 10:00 to 12:00 this does not overlap because we can assign next booking from 10:00.
Please have a look at sql fiddle
sqlfiddle
The logic for overlap for two time periods is:
The first begins before the second ends.
The first ends after the second begins.
In your query, the logic would be:
SELECT count(*) as cnt
FROM orders
WHERE job_date = #job_date AND
job_truck = #truck AND
#start < job_time_end AND
#end > job_time_begin AND
order_status_id IN (1, 5);
Note the use of named parameters. Don't munge query strings with parameter values!. Parameters prevent unexpected and hard-to-debug syntax errors. They also protect the code from SQL injection attacks.
How about this? Not tested, Please check on your DB.
and ( (job_time_beg between '10:00' and '12:00')
or ('10:00' between job_time_beg and job_time_end) )
You can convert between to < and > as per your requirement on edge time.
My goal is it to create a SQL-Query that counts all items in a certain time frame (e.g. 5min)
That's my code so far:
SELECT FROM_UNIXTIME(FLOOR(timestamp_stop/5*60)*(5*60), '%h:%i') AS timekey, timestamp_stop, count(item) AS performance
FROM task
WHERE done = 1
GROUP BY timekey
ORDER BY timestamp_stop ASC
That works great, but doesn't include time frames in which there aren't any records in the database.
I would like to also get these 0-count-ones, up to the current time.
Currently I have no simple/elegant solution in my mind. Any ideas?
Some little post processing in php would also be possible.
As Gordon mentioned, you probably want a secondary table as a basis for ALL 5-minute intervals. I have done similar with a query to self-build using MySQL variables.
select
YourTable.WhateverFields
from
( select
#startTime RangeStart,
#startTime := date_add( #startTime, interval 5 MINUTE ) RangeEnd
from
( select #startTime := '2014-10-20' ) sqlvars,
AnyTableThatHasAsManyDaysYouExpectToReport
limit
12 * numberOfHoursYouNeed * numberOfDaysYouNeed ) DynamicTimeRange
LEFT JOIN YourTable
on YourTable.DateTimeField >= DynamicTimeRange.RangeStart
AND YourTable.DateTimeField < DynamicTimeRange.RangeEnd
So, in this example, the innermost declars a variable "startTime" to Oct 20, 2014 which defaults to 12:00:00. Then the one out from that creates a result set of two columns for a RangeStart and RangeEnd and might look something like...
RangeStart RangeEnd
2014-10-20 00:00 2014-10-20 00:05
2014-10-20 05:00 2014-10-20 00:10
2014-10-20 10:00 2014-10-20 00:15
2014-10-20 05:00 2014-10-20 00:20
2014-10-20 20:00 2014-10-20 00:25
The table reference "AnyTableThatHasAsManyDaysYouExpectToReport" is just that... any table in your database that has at least as many records as you would need to generate your 5-minute intervals for however many hours and days. If you need 1 day worth = 12 records * 5 minutes = 1 hour * 24 hrs = 24*12 = 288 records needed. If you wanted a week, then so be it... multiply that by 7 so my sample just has place-holders to help clarify the intent...
But with the LEFT JOIN, you get all the intervals...
If there are such time frame, but the where clause filters out the records, you can do:
SELECT FROM_UNIXTIME(FLOOR(timestamp_stop/5*60)*(5*60), '%h:%i') AS timekey,
timestamp_stop,
sum(item is not null and done = 1) AS performance
FROM task
GROUP BY timekey
ORDER BY timestamp_stop ASC;
If you still have gaps, then you need to generate a table (or subquery) containing the list of the time frames that you want and use left join.
EDIT:
A subquery is not pleasant. You have to list all the time values. Something like:
SELECT q.timekey, t.timestamp_stop, coalesce(t.performance, 0) as performance
FROM (SELECT '00:00' as timekey UNION ALL
SELECT '00:05' UNION ALL
. . .
) q LEFT JOIN
(SELECT FROM_UNIXTIME(FLOOR(timestamp_stop/5*60)*(5*60), '%h:%i') AS timekey,
timestamp_stop,
COUNT(item) AS performance
FROM task
WHERE done = 1
GROUP BY timekey
) t
ON t.timekey = q.timekey
ORDER BY timestamp_stop ASC;
Table structure is,
Table name: btq_user
name, email, kall, state_id, datain
Now i want to count number records which has kall = 1 or state_id in( 51, 20, 46) by weekly.
i need the results only by weekly (Mon - Sun) no matter if year changes. lets say for example 31-12-2012 is Monday and 6-1-2013 is Sunday so result should include that as a week. no matter if year changes it should count only with weeks.
This is what i tried but not working.
SELECT
count( if( A.state_id = 51 or A.state_id = 20 or A.state_id = 46, A.state_id,
null ) ) AS state_total, count( if( A.kall =1, A.kall, null ) ) AS appointment,
CONCAT( WEEK( A.datain) , " -
", YEAR( A.datain ) ) AS week
FROM btq_user A
GROUP BY week
ORDER BY A.datain ASC
also is there any possibility to display weeks ( 31-12-2012 - 6-1-2013 ) with results ?
Thank you for reading my question.
Two steps to this:
One, you need a week-truncate operation -- that will take your DATETIME item and give back midnight on the preceding Sunday (if your business rule says that the week begins on Sunday).
That becomes a suitable GROUP BY item. The WEEK() / YEAR() hack isn't suitable for this. It really makes a mess in the last/first week of each year.
In my experience, this exxpression will do the week-truncate trick for you, Sunday - Saturday,
FROM_DAYS(TO_DAYS(TIMESTAMP) -MOD(TO_DAYS(TIMESTAMP) -1, 7))
To get Monday - Sunday weeks, use this expression.
FROM_DAYS(TO_DAYS(TIMESTAMP) -MOD(TO_DAYS(TIMESTAMP) -2, 7))
So you can do this.
SELECT COUNT(whatever), SUM(whatelse),
FROM_DAYS(TO_DAYS(event_time) -MOD(TO_DAYS(event_time) -1, 7)) as WEEKSTART,
FROM TABLE
GROUP BY FROM_DAYS(TO_DAYS(event_time) -MOD(TO_DAYS(event_time) -1, 7))
How to group by week in MySQL?
Second, you need to add six days to that truncated date, so you can display the last day of each week along with the first day.
This is a good way to do that, with a nested query
SELECT whats, elses, weekstart, weekstart + INTERVAL 6 DAY AS weekend
FROM (
SELECT COUNT(whatever) AS whats, SUM(whatelse) AS elses,
FROM_DAYS(TO_DAYS(event_time) -MOD(TO_DAYS(event_time) -1, 7)) AS weekstart,
FROM TABLE
GROUP BY FROM_DAYS(TO_DAYS(event_time) -MOD(TO_DAYS(event_time) -1, 7))
) AS summary
ORDER BY weekstart
Doing lots of this? I suggest you create a stored TRUNC_WEEK function.
I have a booking in my mysql booking table bookings as below:
start_date = 2013-03-04
end_date = 2013-03-08
I want to check if a particular day (in this case 2013-03-04) falls between the start and end dates in the booking(s) - BUT i want to add one day to the start date and subtract 1 day from the end date.
So instead of searching between 2013-03-04 and 2013-03-08
i want to search between 2013-03-05 and 2013-03-07
The following query below does the subtract 1 day from the end date but keeps the start date as 04. The search below should give no results but it is still using 04 as the start date and giving my the result of that booking. It is basically searching between [04] [05] [06] [07] when it should do [05] [06] [07]
$fd_query_params = array(
':day' => '2013-03-04
);
$query = "
SELECT
*
FROM
bookings as bb
WHERE
:day BETWEEN
DATE_ADD(bb.start_date, INTERVAL 1 DAY)
AND
DATE_SUB(bb.end_date, INTERVAL 1 DAY)
";
Not php expert myself but should there be more than I notice there is only a single quote around the :day string. It would be good to see the output to verify what the start_date and end_date is
I have a fun one for you. I have a database with the date columns free_from and free_until. What I need to find is the amount of days between now and 1 month today which are free. For example, if the current date was 2013/01/15 and the columns were as follows:
free_from | free_until
2013/01/12| 2013/01/17
2013/01/22| 2013/01/26
2013/01/29| 2013/02/04
2013/02/09| 2013/02/11
2013/02/14| 2013/02/17
2013/02/19| 2013/02/30
The answer would be 16
as 2 + 4 + 6 + 2 + 2 + 0 = 16
The first row only starts counting at the 15th rather than the 12th
since the 15th is the current date.
The last row is discounted because none of the dates are within a
month of the current date.
The dates must be counted as it the free_from date is inclusive and
the free_until date is exclusive.
I'm assuming DATEDIFF() will be used somewhere along the line, but I can't, for the life of me, work this one out.
Thanks for your time!
Edit: This is going into PHP mysql_query so that might restrict you a little concerning what you can do with MYSQL.
SET #today = "2013-01-15";
SET #nextm = DATE_ADD(#today, INTERVAL 1 month);
SET #lastd = DATE_ADD(#nextm, INTERVAL 1 day);
SELECT
DATEDIFF(
IF(#lastd> free_until, free_until, #lastd),
IF(#today > free_from, #today, free_from)
)
FROM `test`
WHERE free_until >= #today AND free_from < #nextm
That should work. At least for your test data. But what day is 2013/02/30? :-)
Dont forget to change #today = CURDATE();
The best I can think of is something like:
WHERE free_until > CURDATE()
AND free_from < CURDATE() + INTERVAL '1' MONTH
That will get rid of any unnecessary rows. Then on the first row do in PHP:
date_diff(date(), free_until)
On the last row, do:
date_diff(free_from, strtotime(date("Y-m-d", strtotime($todayDate)) . "+1 month"))
Then on intermediate dates do:
date_diff(free_from, free_until)
Something to that effect, but this seems extremely clunky and convoluted...
From the top of my mind... first do a:
SELECT a.free_from AS a_from, a.free_until AS a_until, b.free_from AS b_from
FROM availability a
INNER JOIN availability b ON b.free_from > a.free_until
ORDER BY a_from, b_from
This probably will return a set of rows where for each row interval you have next i.e. greater intervals. The results are ordered strategically. You can then wrap the results in a partial group by:
SELECT * FROM (
SELECT a.free_from AS a_from, a.free_until AS a_until, b.free_from AS b_from
FROM availability a
INNER JOIN availability b ON b.free_from > a.free_until
ORDER BY a_from, b_from
) AS NextInterval
GROUP BY a_from, b_until
In the above query, add a DATE_DIFF clause (wrap it in SUM() if necessary):
DATE_DIFF(b_until, a_from)