I have one table called task
id t_title t_started_on t_due_on
1 Test 1 2018-01-18 01:00 PM 2018-01-20 01:00 PM
2 Test 2 2018-01-25 01:00 PM 2018-01-27 01:00 PM
from here i have to select dates like first row start date is 2018-01-18 01:00 PM (t_started_on) and end date is 2018-01-20 01:00 PM(t_due_on).total is 3 days
2018-01-18
2018-01-19
2018-01-20
same as second row also 3 days
2018-01-25
2018-01-26
2018-01-27
Expected Result
Array
(
[allocatedDate] => 2018-01-18
[allocatedDate] => 2018-01-19
[allocatedDate] => 2018-01-20
[allocatedDate] => 2018-01-25
[allocatedDate] => 2018-01-26
[allocatedDate] => 2018-01-27
)
How to write a select query in above case?
Like I said in your previous question, you should not store your dates as strings, but in date datatype: that will make your data less error prone and your queries simpler. Now you'll have to convert those strings to dates each time you need to do date/time calculations with them.
To generate the dates inside periods, you need a helper table, which can be useful also for many other purposes: a table with one column that has natural numbers starting from 0 up to some large n. You could create it like this:
create table nums (i int);
insert into nums values (0), (1), (2), (3);
insert into nums select i+4 from nums;
insert into nums select i+8 from nums;
insert into nums select i+16 from nums;
insert into nums select i+32 from nums;
insert into nums select i+64 from nums;
insert into nums select i+128 from nums;
insert into nums select i+256 from nums;
You can see how you double the number of records by adding a similar insert statement, but this will already generate 512 records, which would be more than enough for your purposes: it should have the highest number of days that a period can have in your tasks table.
Then you can use this query to get the desired output:
SELECT DISTINCT date_add(d_started_on, interval i day)
FROM (
SELECT date(STR_TO_DATE(t_started_on, '%Y-%m-%d')) as d_started_on,
datediff(
date(STR_TO_DATE(t_due_on, '%Y-%m-%d')),
date(STR_TO_DATE(t_started_on, '%Y-%m-%d'))
) as days
FROM tasks
) as base
INNER JOIN nums ON i <= days
ORDER BY 1
See also SQLfiddle
Related
I´m looking for some help with the following problem.
I need to check, whether all selected entries share a common time interval and if so, what is the time interval.
To visualize the problem:
id openingTime closingTime
1 09:00 18:00
2 11:00 15:00
3 12:00 20:00
4 21:00 23:00
Desired output is to get either an empty result or one result with the overlapping interval.
Examples:
selected id openingTime closingTime
1,2 => 11:00 15:00
1,2,3 => 12:00 15:00
1,3 => 12:00 18:00
1,2,3,4 => empty empty
Having IDs with overlapping intervals, the SQL command is easy:
SELECT MAX(openingTime), MIN(closingTime) FROM table WHERE id IN (ids)
But this SQL query doesn't deal with the cases when one or more entries are not sharing the same interval.
Here is some sample data and DB fiddle to try it out:
CREATE TABLE `mytable` (
`id` int(11) NOT NULL,
`openingtime` time NOT NULL,
`closingtime` time NOT NULL
);
INSERT INTO `mytable` (`id`, `openingtime`, `closingtime`) VALUES
(1, '09:00:00', '18:00:00'),
(2, '11:00:00', '15:00:00'),
(3, '12:00:00', '20:00:00'),
(4, '21:00:00', '23:00:00');
Thank you for your help.
D.
I am thinking exists and aggregation:
select min(openingtime), max(closingtime)
from (
select t.*,
exists (
select 1
from mytable t1
where t1.openingtime > t.closingtime or t1.closingtime < t.openingtime
) flag
from mytable t
) t
having max(flag) = 0
The subquery checks if any other row in the table does not overlap with the current row. Then the outer query aggregates, and uses having to filter out the whole result if any row was flagged.
A common interval is going to start at an opening time and end at the next closing time. So, you can test each starting time, counting the number of overlaps.
select o.time, min(t.closingtime)
from (select distinct time from t) o join
t
on o.time >= t.openingtime and o./time <= t.closingtime
group by o.openingtime
having count(*) = (select count(*) from t);
This return no rows if there are no overlaps. It returns all overlapping periods if there is more than one (which I don't think is possible with one row per id).
User input = '2017-03-12'
Let say I have this tableRevenue
date revenue
---------- ---------
2017-01-01 100
2017-01-08 100
2017-01-15 100
2017-01-22 100
2017-01-29 100
2017-01-05 100
2017-01-12 100
2017-02-19 100
2017-02-26 100
2017-03-05 100
2017-03-12 100
And another tableHolidays which contains
date
----------
2017-01-15
2017-02-19
2017-03-05
I want to display it like this:
date revenue
---------- ---------
2017-01-01 100
2017-01-08 100
2017-01-22 100
2017-01-29 100
2017-01-05 100
2017-01-12 100
2017-02-26 100
2017-03-12 100
I want to display the revenue each of the last 8 weeks and I want to skip all the dates that are existing in tableHolidays using a loop. Is this possible in PHP?
mention: you didn't tag any specific database - my answer will refer to SQL-Server:
assuming #UserDate is a variable with the user input date
Use Date-Functions (specific to every DB system) to calculate the date range. in your case to subtract the 8 weeks.
Select all rows within this date range
exclude (NOT IN) all dates from your resultset which occur in your tableHolidays table
GROUP BY weeks (calculate weeknumber with WEEK) and SUM the revenue
Query:
SELECT WEEK(tR.date) as weeknr
,SUM(tR.revenue)
FROM tableRevenue tR
WHERE tR.date >= DATEADD(wk,-8,#UserDate)
AND tR.date <= #UserDate
AND tR.date NOT IN (SELECT date FROM tableHolidays)
GROUP BY WEEK(tR.date)
you can use the 'ANY' which is a mysql operator
for more information you can visit this link
https://www.w3schools.com/sql/sql_any_all.asp
$userInput = '2017-03-12';
$sql = "SELECT `date`, `revenue`
FROM `tableRevenue`
WHERE (
(`date` = ANY
(SELECT `date` FROM `tableHolidays` WHERE DATE(date)<='{$userInput}'))
AND (DATE(date) <='{$userInput}')
)";
Question:
Is it possible to group all rows in MySQL and sum the hours?
Starttime - type:datetime, Stoptime - type:datetime
Data
Starttime, stoptime
2016-10-25 09:00:00, 2016-10-29 17:00:00
2016-11-26 09:00:00, 2016-11-26 17:00:00
2016-11-30 09:00:00, 2016-11-30 17:00:00
2017-01-28 09:00:00, 2017-01-28 17:45:00
The code below sums all the hours between 2016-10-25 and 2017-01-28. I need it to sum all the time on every row and then all rows together. Is it possible to sum this in MySQl or am I bound to PHP?
SELECT
MIN(starttime),
MAX(stoptime),
SUM(TIME_TO_SEC(TIMEDIFF(stoptime, starttime))/3600) AS total_hours
FROM mytable WHERE id = 1219
To sum up all differences, you don't need min(starttime) or max(stoptime), just use sum of timestampdiff, e.g.
select sum(timestampdiff(hour, starttime, stoptime))
from mytable
If you want fractional hours, keep time_to_sec(...) / 3600 as in your question
select sum(time_to_sec(timediff(stoptime, starttime)) / 3600)
from mytable
See also sqlfiddle
I have a MySQL database with data recorded every 15 minutes. For simplicity, lets assume there are 2 fields:
DATETIME Created
Double Value
I would like to draw a chart which needs for each hour the opening, min, max, and closing values for an hour. To do this I need to return results from my MySQL query to my PHP to create a JSON. I would like to do this in the MySQL query so that the response is cached.
Here is an example of the problem, given 9 data points trying to get 2 hour groups:
Creation Value
2014-03-25 12:15:00 413.17011
2014-03-25 12:00:00 414
2014-03-25 11:45:00 415
2014-03-25 11:30:00 415
2014-03-25 11:15:00 415.5
2014-03-25 11:00:00 415.5
2014-03-25 10:45:00 416
2014-03-25 10:30:00 416
2014-03-25 10:15:00 415.99
I would need:
Hour 1 (11:15:00 to 12:15:00)
Open: 415.5
Close: 413.17011
High: 415.5
Low: 413.17011
Hour 2 (10:15:00 to 11:15:00)
Open: 415.99
Close: 415.5
High: 416
Low: 415.5
Of course for the full 24 hours this would need repeating, this is just an example.
Any help is really appreciated!
Here is the current MySQL dump for the example (Using MySQL version 2.6.4-pl3):
--
-- Table structure for table `exampleTable`
--
CREATE TABLE `exampleTable` (
`created` datetime NOT NULL,
`value` double NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
--
-- Dumping data for table `exampleTable`
--
INSERT INTO `exampleTable` VALUES ('2014-03-25 12:15:00', 413.17011);
INSERT INTO `exampleTable` VALUES ('2014-03-25 12:00:00', 414);
INSERT INTO `exampleTable` VALUES ('2014-03-25 11:45:00', 415);
INSERT INTO `exampleTable` VALUES ('2014-03-25 11:30:00', 415);
INSERT INTO `exampleTable` VALUES ('2014-03-25 11:15:00', 415.5);
INSERT INTO `exampleTable` VALUES ('2014-03-25 11:00:00', 415.5);
INSERT INTO `exampleTable` VALUES ('2014-03-25 10:45:00', 416);
INSERT INTO `exampleTable` VALUES ('2014-03-25 10:30:00', 416);
INSERT INTO `exampleTable` VALUES ('2014-03-25 10:15:00', 415.99);
Get it to work
You might try
SELECT
DATE(created) AS day,
HOUR(created) AS hour,
(
SELECT Value FROM `table` AS b
WHERE DATE(a.created) = DATE(b.created)
AND HOUR(a.created) = HOUR(b.created)
ORDER BY created ASC LIMIT 1
) AS Open,
(
SELECT Value FROM `table` AS b
WHERE DATE(a.created) = DATE(b.created)
AND HOUR(a.created) = HOUR(b.created)
ORDER BY created DESC LIMIT 1
) AS Close,
MIN(value) AS Low,
MAX(value) AS High
FROM `table` AS a
GROUP BY DATE(created), HOUR(created)
this groups all your rows by DATE+HOUR and computes the MIN respectively MAX as Low or High. To find the first and last row for Open and Close, the easiest in SQL syntax is a subselect. It selects all rows which are relevant for the current row, and sorts them ascending or descending. Then selects the first row.
Please consider that this groups only by hour. Instead of
Hour 1 (11:15:00 to 12:15:00)
Hour 2 (10:15:00 to 11:15:00)
this groups like
Hour 1 (11:00:00 to 11:59:00)
Hour 2 (10:00:00 to 10:59:00)
If you want to keep the 15 minutes offset, you may subtract this from your created timestamp (created - INTERVAL 15 MINUTE) at all occurrences of created in the sql query above.
I created a working sqlfiddle for you.
Performance
Just as hint: If you can, you might want to split date and time into two columns (of types date and time). This way you do not need to cast DATE() on created everytime, but can use the new date column instead. You can then add a combined index to this new columns too, which speeds up your query. See this sqlfiddle for an example.
To get your grouping right, you can use
FLOOR(( UNIX_TIMESTAMP(myTable.dateCreated) - 900 ) / 3600)
where 3600 sets the interval at 1 hour and the - 900 sets the offset at 00:15
Since you need the MIN() and MAX for each of your four values, you'll need to JOIN the main table to itself but grouped by the min or max (based on the column).
finally, you have each sub-query (joined table) calculate the grouping hour above so you can use that to join them. Here's what I cam up with (with slightly different column names and
SELECT openDate,Open,Close,High,Low
FROM (SELECT FLOOR(( UNIX_TIMESTAMP(myTable.dateCreated) - 900 ) / 3600)
AS
theHour,
myTable.value AS Open,myTable.dateCreated openDate
FROM myTable
JOIN (SELECT value,MIN(dateCreated) AS dateCreated
FROM myTable
GROUP BY FLOOR(( UNIX_TIMESTAMP(dateCreated) - 900 )
/ 3600)
) AS
aggTable
ON aggTable.dateCreated = myTable.dateCreated) AS
openTable
LEFT JOIN (SELECT FLOOR(( UNIX_TIMESTAMP(myTable.dateCreated) - 900
) /
3600) AS
theHour
,
myTable.value AS Close,myTable.dateCreated closeDate
FROM myTable
JOIN (SELECT value,MAX(dateCreated) AS dateCreated
FROM myTable
GROUP BY FLOOR(( UNIX_TIMESTAMP(dateCreated) - 900 ) / 3600)
) AS
aggTable
ON aggTable.dateCreated = myTable.dateCreated) AS closeTable
ON openTable.theHour = closeTable.theHour
LEFT JOIN (SELECT
FLOOR((
UNIX_TIMESTAMP(myTable.dateCreated) - 900 ) / 3600) AS
theHour,
MAX(
value)
AS High
FROM myTable
GROUP BY theHour) AS highTable
ON closeTable.theHour = highTable.theHour
LEFT JOIN (SELECT
FLOOR((
UNIX_TIMESTAMP(myTable.dateCreated) - 900 ) / 3600) AS
theHour,
MIN(
value)
AS Low
FROM myTable
GROUP BY theHour) AS lowTable
ON highTable.theHour = lowTable.theHour
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