Aligning timestamps when not quite synchronized - mysql

I have 3 processes A, B and C as defined in the following series of tables:
http://sqlfiddle.com/#!2/48f54
CREATE TABLE processA
(date_time datetime, valueA int);
INSERT INTO processA
(date_time, valueA)
VALUES
('2013-1-8 22:10:00', 100),
('2013-1-8 22:15:00', 100),
('2013-1-8 22:30:00', 100),
('2013-1-8 22:35:00', 100),
('2013-1-8 22:40:00', 100),
('2013-1-8 22:45:00', 100),
('2013-1-8 22:50:00', 100),
('2013-1-8 23:05:00', 100),
('2013-1-8 23:10:00', 100),
('2013-1-8 23:20:00', 100),
('2013-1-8 23:25:00', 100),
('2013-1-8 23:35:00', 100),
('2013-1-8 23:40:00', 100),
('2013-1-9 00:05:00', 100),
('2013-1-9 00:10:00', 100);
CREATE TABLE processB
(date_time datetime, valueB decimal(4,2));
INSERT INTO processB
(date_time, valueB)
VALUES
('2013-1-08 21:46:00', 3),
('2013-1-08 22:11:00', 4),
('2013-1-08 22:31:00', 5),
('2013-1-08 22:36:00', 6),
('2013-1-08 22:41:00', 7),
('2013-1-08 23:06:00', 8),
('2013-1-08 23:20:00', 2),
('2013-1-08 23:46:00', 3),
('2013-1-09 00:34:00', 9);
CREATE TABLE processC
(date_time datetime, status varchar(4));
INSERT INTO processC
VALUES
('2013-1-08 18:00:00', 'yes'),
('2013-1-08 19:00:00', 'yes'),
('2013-1-08 20:00:00', 'yes'),
('2013-1-08 21:00:00', 'yes'),
('2013-1-08 22:00:00', 'yes'),
('2013-1-08 23:00:00', 'no'),
('2013-1-08 00:00:00', 'no'),
('2013-1-08 01:00:00', 'no');
As you can see the time at which readings occur for each of the processes is not the same.
ProcessA, IF it occurs, does so at 5 minute intervals
ProcessB, readings occur at unpredictable times but usually occur multiple times within the hour
ProcessC will always have an hourly value (yes or no).
Firstly, I want to convert processB so that there is a reading at ever 5 minute interval so the data aligns with processA, which can then enable me to do a simple join of both tables at the 5 minute interval mark. For the conversion, the data at every 5 minutes should be set to the nearest processB observation available within [-30,30) minute window. If values are equidistant then take the average. If none is available in the 30 minute window then set it to null.
Once I have that, I can do a simple join on %Y%m%d%H with ProcessC using something like the following to get a final table with all data aligned at the 5 minute interval mark:
date_format(date_time, '%Y%m%d%H') = date_format(date_time, '%Y%m%d%H')
If anyone has any pointers/guidance I would appreciate some direction. I appreciate it.
Sample output:
'2013-1-8 22:10:00', 100, 4, yes <--- closer to 22:11 than 21:46
'2013-1-8 22:15:00', 100, 4, yes <--- closer to 22:11 than 21:31
'2013-1-8 22:30:00', 100, 5, yes <--- closer to 22:31 than 22:11
'2013-1-8 22:35:00', 100, 6, yes <--- closer to 22:36 than 22:31
'2013-1-8 22:40:00', 100, 7, yes <--- closer to 22:41 than 22:36
'2013-1-8 22:45:00', 100, 7, yes <--- closer to 22:41 than 23:06
'2013-1-8 22:50:00', 100, 7, yes <--- closer to 22:41 than 23:06
'2013-1-8 23:05:00', 100, 8, yes <--- closer to 23:06 than 23:06
'2013-1-8 23:10:00', 100, 8, no <--- closer to 23:06 than 23:20
'2013-1-8 23:20:00', 100, 2, no <--- closer to 23:20 than 23:10
'2013-1-8 23:25:00', 100, 2, no <--- closer to 23:20 than 23:10
'2013-1-8 23:35:00', 100, 3, no <--- closer to 23:46 than 23:20
'2013-1-9 00:05:00', 100, 3, no <--- closer to 23:46 than 00:34
'2013-1-9 00:10:00', 100, 6, no <--- takes the avg of 3 and 9

The tricky part of this is the retrieval of the appropriate row or rows from processB that correspond to each row of processA as you figured out.
Let's take it step by step.
First, we need to be able to join processA and processB to retrieve the candidate timestamp pairs. Let's do it like this:
SELECT a.date_time a,
TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff
FROM processA a
JOIN processB b
ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800
AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) < 1800
This gets us the a and b times meeting the [-30, 30) criterion. There are a lot of rows in this result; but we can inspect it to make sure we've done the range comparison correctly. http://sqlfiddle.com/#!2/48f54/47/0
Now we need to generate the time window to search for each a record for your one or more matching b records. Like so.
SELECT a,
MIN(ABS(timediff)) windowsize
FROM (
SELECT a.date_time a,
TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff
FROM processA a
JOIN processB b
ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800
AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) < 1800
) d
GROUP BY a
This yields two columns: the first is the timestamp from a, and the second is the time range of the nearest b timestamp (or timestamps, if more than one are to be averaged) that are in range. This resultset doesn't have any row for a records that don't have b records near enough to consider. http://sqlfiddle.com/#!2/48f54/46/0
Finally, we need to retrieve and average the b record values for each a record. Here this is.
SELECT processA.date_time date_time,
processA.valueA valueA,
AVG(processB.valueB) valueB
FROM processA
LEFT JOIN (
SELECT a,
MIN(ABS(timediff)) windowsize
FROM (
SELECT a.date_time a,
TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) timediff
FROM processA a
JOIN processB b
ON TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) >= -1800
AND TIMESTAMPDIFF(SECOND, a.date_time, b.date_time) < 1800
) d
GROUP BY a
) j ON processA.date_time = j.a
LEFT JOIN processB ON ( processB.date_time >= j.a - INTERVAL j.windowsize SECOND
AND processB.date_time <= j.a + INTERVAL j.windowsize SECOND
AND processB.date_time < j.a + INTERVAL 1800 SECOND)
GROUP BY processA.date_time, processA.valueA
Notice there are a couple of open ranges here (< operators instead of <= operators). Those are there to accomodate your [-30, 30) open range. Here's the query. http://sqlfiddle.com/#!2/48f54/45/0
This final query joins together three tables: processA, our virtual table showing the search range for each timestamp, and process B. The last ON clause performs the actual range search. It's made slightly more complicated by the open range.
See how this goes? It's helpful to construct the query from the inside out.
Don't forget to put an index on processB.date_time.
I am taking the liberty of leaving the join of processC to this virtual table to you.

Related

MYSQL JOIN with lower-than / greater-than conditions (minimum_quantity, valid_from)

I have
order table with columns
id
date
supplier_id
order_lineitem table with columns
id
order_id
article_id
order_quantity
order_price
a prices table with columns
id
article_id
supplier_id
valid_until
minimum_order_quantity
list_price
The prices table doesn't necessarily have to have a matching / valid entry, so this one would have to be joined via an outer join.
I'd like to compare order_prices against list_prices.
Therefore I need to somehow join
SELECT
o.id,
o.date,
ol.article_id,
ol.order_quantity,
ol.order_price,
p.list_price
FROM
`order` o JOIN order_lineitem ol on ol.order_id = o.id
LEFT OUTER JOIN prices p on
p.article_id = ol.article_id
AND p.supplier_id = o.supplier_id
AND p.minimum_order_quantity <= ol.order_quantity
AND IFNULL(p.valid_until, DATE('2099-12-31')) >= o.date
/* here comes the fun part that doesn't work (reliably) */
ORDER BY
IFNULL(p.valid_until, DATE('2099-12-31')) asc,
p.minimum_order_quantity desc
GROUP BY o.id, ol.id, p.article_id
/* ... trying to get only THAT price from the prices table that applies for the
(a) the given article
(b) from the given supplier
(c) that was valid at the time of purchase (i.e. has the smallest "valid_until" date that is greater than the purchase date)
(d) when ordering the given quantity (prices can also increase with higher quantities, so it has to be the price with the largest minimum_order_quantity that is smaller than the ordered quantity)
*/
I particularly don't want to fall into the trap (which I dug for myself here) of using group by to limit the results to 1 record from the prices table based on a previous sorting, since
(i) as per MySQL documentation it is non-deterministic which record will actually get returned (although it may in effect often work and this is a frequently suggested route to go) - also see this excellent explanation on the issue: https://stackoverflow.com/a/14770936/9818188 and
(ii) this concept wouldn't work on other SQL implementations like SQL Server, Maria DB & Co.
The question is not around putting in a nested query in order to be able to ORDER first and then GROUP subsequently. It's more about how to really properly get the correct row--ideally also working on other SQL implementations like SQL Server, Maria DB or Google BigQuery.
And since I can't really rely on prices being cheaper the more I buy I also can't simply get the min(list_price).
How can this can be achieved?
Since the output of this query is required for downstream processing, I can't slice & dice the task but need a full list of all orders with respective list prices.
EDIT
Here is a SQL fiddle - the desired prices are shown in column order_price, the prices incorrectly determined by the JOIN (excluding the order byclause - as this would cause non-deterministic results) are shown in column list_price:
http://sqlfiddle.com/#!9/f03a4f/2
CREATE TABLE `order`
(`id` int, `date` datetime, `supplier_id` int)
;
INSERT INTO `order`
(`id`, `date`, `supplier_id`)
VALUES
(1, '2022-01-15 00:00:00', 1),
(2, '2022-02-15 00:00:00', 1),
(3, '2022-03-15 00:00:00', 1),
(4, '2022-01-15 00:00:00', 2),
(5, '2022-02-15 00:00:00', 2),
(6, '2022-03-15 00:00:00', 2)
;
CREATE TABLE order_lineitem
(`id` int, `order_id` int, `article_id` int, `order_quantity` int, `order_price` int)
;
INSERT INTO order_lineitem
(`id`, `order_id`, `article_id`, `order_quantity`, `order_price`)
VALUES
(1, 1, 1, 1, 11),
(2, 1, 1, 10, 8),
(3, 1, 1, 100, 9),
(4, 2, 1, 1, 15),
(5, 2, 1, 10, 12),
(6, 2, 1, 100, 13),
(7, 3, 1, 1, 17),
(8, 3, 1, 10, 14),
(9, 3, 1, 100, 16),
(10, 4, 1, 1, 10),
(11, 4, 1, 10, 80),
(12, 4, 1, 100, 80),
(13, 5, 1, 1, 10),
(14, 5, 1, 10, 80),
(15, 5, 1, 100, 80),
(16, 6, 1, 1, 10),
(17, 6, 1, 10, 10),
(18, 6, 1, 100, 10)
;
CREATE TABLE prices
(`id` int, `article_id` int, `supplier_id` int, `valid_until` varchar(10), `minimum_order_quantity` int, `list_price` int)
;
INSERT INTO prices
(`id`, `article_id`, `supplier_id`, `valid_until`, `minimum_order_quantity`, `list_price`)
VALUES
(1, 1, 1, '2022-01-31', 1, 11),
(2, 1, 1, '2022-01-31', 10, 8),
(3, 1, 1, '2022-01-31', 100, 9),
(4, 1, 2, NULL, 1, 10),
(5, 1, 1, '2022-02-31', 1, 15),
(6, 1, 1, '2022-02-31', 10, 12),
(7, 1, 1, '2022-02-31', 100, 13),
(8, 1, 1, NULL, 1, 17),
(9, 1, 1, NULL, 10, 14),
(10, 1, 1, NULL, 100, 16),
(11, 2, 1, NULL, 1, 99),
(12, 1, 2, '2022-02-31', 10, 80)
;
SELECT
o.id,
o.supplier_id,
o.date,
ol.article_id,
ol.order_quantity,
ol.order_price,
p.list_price
FROM
`order` o JOIN order_lineitem ol on ol.order_id = o.id
LEFT OUTER JOIN prices p on
p.article_id = ol.article_id
AND p.supplier_id = o.supplier_id
AND p.minimum_order_quantity <= ol.order_quantity
AND IFNULL(p.valid_until, DATE('2099-12-31')) >= o.date
/* here comes the fun part that doesn't work (reliably) */
/* NOTE: I am purposesly commenting out the ORDER BY clause here, because
(a) it would have to go after GROUP BY - requiring a nested table which I would like to prevent AND, more importantly,
(b) limiting the numer of rows returned to 1 by GROUPing with an incomplete set of columns on a sorted table may return non-deterministic results as per the MySQL documentation.
see also https://stackoverflow.com/a/14770936/9818188 explaining the issue with GROUP BY in this context
#
# ORDER BY
# IFNULL(p.valid_until, DATE('2099-12-31')) asc,
# p.minimum_order_quantity desc
*/
GROUP BY o.id, ol.id, p.article_id
/* ... trying to get only THAT price from the prices table that applies for the
(a) the given article
(b) from the given supplier
(c) that was valid at the time of purchase (i.e. has the smallest "valid_until" date that is greater than the purchase date)
(d) when ordering the given quantity (prices can also increase with higher quantities, so it has to be the price with the largest minimum_order_quantity that is smaller than the ordered quantity)
*/
If you are interrestd in the highest listprice, you would do it like the.
If you need also other columns from theprices table, you need to SQL select only rows with max value on a column
as you have to join the sub querys for all articles
SELECT
o.id,
o.date,
ol.article_id,
ol.order_quantity,
ol.order_price,
(SELECT `list_price` FROM prices p WHERE
p.article_id = ol.article_id
AND p.supplier_id = o.supplier_id
AND p.minimum_order_quantity <= ol.order_quantity
AND IFNULL(p.valid_until, DATE('2099-12-31')) >= o.date
ORDER BY `list_price` DESC
LIMIT 1
) list_price
FROM
`order` o JOIN order_lineitem ol on ol.order_id = o.id

use group by max() twice in another column

I use mysql query.
I want to know the date and amount of the highest salary.
I want to know if I can use max() twice as below.
The result comes out exactly as I want.
But as far as I know, you have to use only one max().
Was I mistaken?
http://sqlfiddle.com/#!9/8db0c17/4
create table test ( mid bigint , sal bigint , dt date);
insert into test values( 1, 100, '2020-01-01 00:00:00'),
( 1, 200, '2020-02-01 00:00:00'),
( 2, 100, '2020-03-01 00:00:00'),
( 2, 200, '2020-04-01 00:00:00'),
( 2, 300, '2020-05-01 00:00:00'),
( 3, 500, '2020-10-01 00:00:00');
select mid, max(sal), max(dt) from test group by mid;
mid max(sal) max(dt)
1 200 2020-02-01
2 300 2020-05-01
3 500 2020-10-01
You can use max several times in your query, but in your case, you will not get what you want.
If we change your data like this:
1, 100, 2020-01-01
1, 200, 2020-02-01
2, 100, 2020-02-01
2, 300, 2020-03-01
2, 200, 2020-04-01
2, 300, 2020-05-01
3, 500, 2020-10-01
3, 300, 2020-11-01
Your result will be:
1, 200, 2020-02-01
2, 300, 2020-05-01
3, 500, 2020-11-01
As you can see, for 3rd row we get maximum value for sal and for dt but separately.
We can use somthing like this to get the right result:
select
t.mid, max(t.dt), tmp.sal_max
from test t
join (
select t1.mid, max(t1.sal) as sal_max
from test t1
group by t1.mid) tmp on tmp.mid = t.mid AND tmp.sal_max = t.sal
group by tmp.mid;
Result:
1, 2020-02-01, 200
2, 2020-05-01, 300
3, 2020-10-01, 500
I think it is not the simplest option, but it will work.
Yes you can use max many times it should be different columns names
The other column should be in group by

Cumulative sum with max date group by month year and id

Now i need to make similar query but need to several criteria
Here is my table
`transaksi` (`transid`, `idpinj`, `tanggal`,`sisapokok`, `sisajasa`
(1, 1, '2018-01-01', 1000, 100, 1),
(2, 1, '2018-01-05', 1000, 100, 3),
(3, 2, '2018-02-04', 1000, 100, 4),
(4, 2, '2018-02-08', 1000, 100, 5),
(5, 1, '2018-02-19', 1000, 100, 3),
(6, 3, '2018-02-22', 1000, 100, 2),
(7, 2, '2018-03-09', 1000, 100, 3),
(8, 3, '2018-03-10', 1000, 100, 3)
(9, 3, '2018-03-12', 1000, 100, 4)
(10, 1, '2018-03-17', 1000, 100, 4)
(11, 4, '2018-03-19', 1000, 100, 3)
(12, 2, '2018-03-20', 1000, 100, 4)
DB Fiddle table
From the table above i need to get output as follow
Month sisapokok sisajasa
Jan-2018 1000 100 ->row2
Feb-2018 4000 400 ->+ row3+5
Mar-2018 12000 1200 ->+ row9+10+11+12
First I need to get sum(sisapokok) and sum(sisajasa) for each idpinj where date is max(tanggal), status between 3 and 4. This value then sum as total per month
Make cumulative sum each month for the last 12 month
I try this query but it get the max(date) from all records not max(date) by month and each idpinj.
SELECT a.idpinj,a.sisapokok
FROM transaksi a
INNER JOIN
(
SELECT idpinj, MAX(tanggal) tgl
FROM transaksi
GROUP BY idpinj
) b ON a.idpinj = b.idpinj
AND a.tanggal = b.tgl
ORDER BY `a`.`idpinj` ASC
Not sure exactly what you are asking for but see if this helps:
select monthyear, sum(sisapokok)sisapokok, sum(sisajasa)sisajasa from (
select cast(month(tanggal) as varchar)+'-'+cast(year(tanggal) as varchar) monthyear, sum(sisapokok)sisapokok, sum(sisajasa)sisajasa
from #transaksi
group by cast(month(idpinj) as varchar)+'-'+cast(year(tanggal) as varchar) , tanggal) a
group by monthyear
Based on the fiddle data
select yyyy,mm,
#s:=#s+sisapokok sisapokok,
#t:=#t+sisajasa sisajasa
from
(
select yyyy,mm,sum(sisapokok) sisapokok,sum(sisajasa) sisajasa
from
(
select year(tanggal) yyyy,month(tanggal) mm, sisapokok,sisajasa
from transaksi t
join
(
select year(tanggal) yyyy,month(tanggal) mm,idpinj,max(transid) maxid
from `transaksi`
where status in(3,4)
group by year(tanggal),month(tanggal),idpinj
) s on s.maxid = transid
) t
group by yyyy,mm
) u
,(select #s:=0,#t:=0) r
order by yyyy,mm
+------+------+-----------+----------+
| yyyy | mm | sisapokok | sisajasa |
+------+------+-----------+----------+
| 2018 | 1 | 2000 | 2003 |
| 2018 | 2 | 5000 | 2303 |
| 2018 | 3 | 13000 | 3103 |
+------+------+-----------+----------+
3 rows in set (0.00 sec)
Note the inner query finds the last relevant id and the code progresses outward to use variables to calculate running totals.

Mysql return 0 not null in sum

Help needed please I have a table with taskId, Materials, Labour and a table with expenses in. The issue i have is that some tasks do not have and expense column in the taskenpense table so the column returns null. I need null to be 0.
` CREATE TABLE emptasks ( empTaskId INT, taskMaterials NUMERIC(8,2),taskLabour NUMERIC(8,2));
INSERT INTO emptasks VALUES
(1, 50, 50),
(2, 450.26, 50),
(3, 2505.10, 50),
(4, 2505.10, 50),
(5, 500, 500),
(6, 1000, 50);
CREATE TABLE taskexpenses (
feeID INT,
empTaskId INT,
expense NUMERIC(8,2));
INSERT INTO taskexpenses VALUES
(1, 1, 50.00),
(1, 2, 50.00),
(2, 2, 126.00),
(3, 3, 50.00),
(4, 4, 50.00),
(2, 2, 1206.00);
SELECT
p.empTaskId,
p.Labour,
p.Materials,
f.Expenses,
p.Labour + p.Materials - f.Expenses AS Total,
ROUND( (f.Expenses + p.Materials) / p.Labour * 100, 2) AS Percentage
FROM (
SELECT
empTaskId,
SUM(taskMaterials) AS Labour,
SUM(taskLabour) AS Materials
FROM emptasks
GROUP BY empTaskId
) p
LEFT JOIN (
SELECT taskexpenses.empTaskId,
SUM(expense) AS Expenses
FROM emptasks
INNER JOIN taskexpenses ON emptasks.empTaskId = taskexpenses.empTaskId
GROUP BY empTaskId
) f ON p.empTaskId = f.empTaskId
the result is
empTaskId Labour Materials Expenses Total Percentage
1 50 50 50 50 200
2 450.26 50 1382 -881.74 318.04
3 2505.1 50 50 2505.1 3.99
4 2505.1 50 50 2505.1 3.99
5 500 500 (null) (null) (null)
6 1000 50 (null) (null) (null)
I need the null value to return 0 so the sum can be worked out
FIDDLE LINK
THanks
Jon
Slightly simpler than in the answer from #Bob Jarvis is to use the IFNULL() function.
SELECT
p.empTaskId,
p.Labour,
p.Materials,
IFNULL(f.Expenses, '0') AS Expenses,
IFNULL(p.Labour + p.Materials - f.Expenses, '0') AS Total,
IFNULL(ROUND( (f.Expenses + p.Materials) / p.Labour * 100, 2), '0') AS Percentage
FROM ...
See fiddle
Use the COALESCE function:
SELECT p.empTaskId,
p.Labour,
p.Materials,
COALESCE(f.Expenses, 0) AS Expenses,
COALESCE(p.Labour, 0) + COALESCE(p.Materials, 0) - COALESCE(f.Expenses, 0) AS Total,
ROUND( (COALESCE(f.Expenses, 0) + COALESCE(p.Materials, 0)) / p.Labour * 100, 2) AS Percentage
FROM (SELECT empTaskId,
SUM(COALESCE(taskMaterials, 0)) AS Labour,
SUM(COALESCE(taskLabour, 0)) AS Materials
FROM emptasks
GROUP BY empTaskId) p
LEFT JOIN (SELECT taskexpenses.empTaskId,
SUM(COALESCE(expense, 0)) AS Expenses
FROM emptasks
INNER JOIN taskexpenses
ON emptasks.empTaskId = taskexpenses.empTaskId
GROUP BY empTaskId) f
ON p.empTaskId = f.empTaskId
Note that here I've put COALESCE on just about everything which might possibly be NULL. If you only want to put it on the Expenses column change it to be what you want.
Best of luck.

Select and show business open hours from MySQL

I dont need to check if business is open or close, but I need to show open hours by days.
There are some options:
1 - Business open once in day (sample - from 10:00 to 18:30) - one
rows in table
2 - Business open TWICE in day (samlpe - from 10:00 to
14:00 and from 15:00 to 18:30) - two rows in table
3 - Business may
be closed (no row inserted)
Here my MySql table of hours storing. In this sample business (affiliate_id) are open twice in days from 0 to 4, once in day 5 and closed in day 6 (no records for this day)
http://postimage.org/image/yplj4rumj/
What I need to show in website its like (according to this database example:
0,1,2,3,4 - open 10:00-14:00 and 15:00-18:30
5 - open 10:00-12:00
6 - closed
How I get results like:
http://postimage.org/image/toe53en63/
?
I tried to make queries with GROUPֹ_CONCAT and LEFT JOIN the same table ON a.day=b.day but with no luck :(
There sample of my query (that is wrong)
SELECT GROUP_CONCAT( DISTINCT CAST( a.day AS CHAR )
ORDER BY a.day ) AS days, DATE_FORMAT( a.time_from, '%H:%i' ) AS f_time_from, DATE_FORMAT( a.time_to, '%H:%i' ) AS f_time_to, DATE_FORMAT( b.time_from, '%H:%i' ) AS f_time_from_s, DATE_FORMAT( b.time_to, '%H:%i' ) AS f_time_to_s
FROM business_affiliate_hours AS a LEFT
JOIN business_affiliate_hours AS b ON a.day = b.day
WHERE a.affiliate_id =57
GROUP BY a.time_from, a.time_to, b.time_from, b.time_to
ORDER BY a.id ASC
This my table:
CREATE TABLE IF NOT EXISTS `business_affiliate_hours` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`affiliate_id` int(10) unsigned NOT NULL DEFAULT '0',
`time_from` time NOT NULL,
`time_to` time NOT NULL,
`day` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
INSERT INTO `business_affiliate_hours` (`id`, `affiliate_id`, `time_from`, `time_to`, `day`) VALUES
(53, 57, '10:00:00', '12:00:00', 5),
(52, 57, '15:00:00', '18:30:00', 4),
(51, 57, '10:00:00', '14:00:00', 4),
(50, 57, '15:00:00', '18:30:00', 3),
(49, 57, '10:00:00', '14:00:00', 3),
(48, 57, '15:00:00', '18:30:00', 2),
(47, 57, '10:00:00', '14:00:00', 2),
(46, 57, '15:00:00', '18:30:00', 1),
(45, 57, '10:00:00', '14:00:00', 1),
(44, 57, '15:00:00', '18:30:00', 0),
(43, 57, '10:00:00', '14:00:00', 0);
Open hours may be different for every day, so I want to GROUP by the same open hours, and get list of days for all unique order of open hours.
Need your help!
Sorry for links to images, I cant upload images yes to here.
First build a materialised table of each day's combined times, then group on that:
SELECT GROUP_CONCAT(day ORDER BY day) AS days,
DATE_FORMAT(f1, '%H:%i') AS f_time_from,
DATE_FORMAT(t1, '%H:%i') AS f_time_to,
DATE_FORMAT(f2, '%H:%i') AS f_time_from_s,
DATE_FORMAT(t2, '%H:%i') AS f_time_to_s
FROM (
SELECT day,
MIN(time_from) AS f1,
MIN(time_to ) AS t1,
IF(COUNT(*) > 1, MAX(time_from), NULL) AS f2,
IF(COUNT(*) > 1, MAX(time_to ), NULL) AS t2
FROM business_affiliate_hours
WHERE affiliate_id = 57
GROUP BY day
) t
GROUP BY f1, t1, f2, t2
ORDER BY days
See it on sqlfiddle.