I am trying to figure out how to group a query result where the final Grouping should happen where the time difference is less than let's say one minute.
I have watermeter that logs my water usage and I am trying to group the results so that the graphs will make more sense. My sql queries for grouping the water usage per Year, Month, Day and hour are perfect, but then I would like to drill down to where the final result shows me a grouping where as an example I water the grass.
My Table Structure looks like:
id liter total_liters date time dater
9 3 184 2020/12/06 16:14:58 2020/12/06 16:14
10 1 185 2020/12/06 16:15:04 2020/12/06 16:15
11 3 188 2020/12/06 16:26:49 2020/12/06 16:26
12 2 190 2020/12/06 16:26:55 2020/12/06 16:26
13 2 192 2020/12/06 16:27:01 2020/12/06 16:27
14 1 193 2020/12/06 17:32:16 2020/12/06 17:32
15 1 194 2020/12/06 17:32:22 2020/12/06 17:32
16 1 195 2020/12/06 17:32:28 2020/12/06 17:32
17 1 196 2020/12/06 17:32:35 2020/12/06 17:32
18 1 197 2020/12/06 17:32:41 2020/12/06 17:32
19 1 198 2020/12/06 17:32:47 2020/12/06 17:32
20 1 199 2020/12/06 17:32:53 2020/12/06 17:32
21 1 200 2020/12/06 17:32:59 2020/12/06 17:32
22 1 201 2020/12/06 17:35:05 2020/12/06 17:35
23 1 202 2020/12/06 17:35:17 2020/12/06 17:35
24 1 203 2020/12/06 17:35:23 2020/12/06 17:35
25 1 204 2020/12/06 17:35:29 2020/12/06 17:35
26 1 205 2020/12/06 17:35:41 2020/12/06 17:35
27 1 206 2020/12/06 17:43:05 2020/12/06 17:43
28 3 209 2020/12/06 17:43:11 2020/12/06 17:43
29 2 211 2020/12/06 17:43:17 2020/12/06 17:43
30 2 213 2020/12/06 17:43:23 2020/12/06 17:43
31 2 215 2020/12/06 17:43:29 2020/12/06 17:43
32 3 218 2020/12/06 17:43:36 2020/12/06 17:43
33 2 220 2020/12/06 17:43:42 2020/12/06 17:43
And my current query looks like:
SELECT DATE_FORMAT(dater,'%H:%i') AS dater,
YEAR(dater),
MONTHNAME(dater),
DAY(dater),
HOUR(dater),
MINUTE(dater),
SUM(liter) as liter
FROM watermeter
WHERE date LIKE '2020-12-08'
GROUP BY YEAR(date), MONTHNAME(date), DAY(dater), HOUR(dater), MINUTE(dater)
ORDER BY id ASC`
The result should be to sum the Liters together by grouping them by Year then Month then Day then Hour and then it should group the results where the time difference is less than 60 seconds.
I might end up by grouping them Year, Month, Day and then by time difference is less than 60 seconds .
Like
2020-12-06 17:35:05 5 Liters
2020-12-06 17:43:05 13 Liters
Here is a phpmyaddmin sql dump if it helps
-- phpMyAdmin SQL Dump
-- version 4.6.6deb5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Dec 10, 2020 at 07:27 AM
-- Server version: 10.3.17-MariaDB-0+deb10u1
-- PHP Version: 7.3.11-1~deb10u1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET #OLD_CHARACTER_SET_CLIENT=##CHARACTER_SET_CLIENT */;
/*!40101 SET #OLD_CHARACTER_SET_RESULTS=##CHARACTER_SET_RESULTS */;
/*!40101 SET #OLD_COLLATION_CONNECTION=##COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `mysensors`
--
-- --------------------------------------------------------
--
-- Table structure for table `watermeter`
--
CREATE TABLE `watermeter` (
`id` int(10) NOT NULL,
`liter` int(11) NOT NULL,
`total_liters` int(11) NOT NULL,
`date` date NOT NULL,
`time` time NOT NULL,
`dater` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `watermeter`
--
INSERT INTO `watermeter` (`id`, `liter`, `total_liters`, `date`, `time`, `dater`) VALUES
(9, 3, 184, '2020-12-06', '16:14:58', '2020-12-06 16:14:58'),
(10, 1, 185, '2020-12-06', '16:15:04', '2020-12-06 16:15:04'),
(11, 3, 188, '2020-12-06', '16:26:49', '2020-12-06 16:26:49'),
(12, 2, 190, '2020-12-06', '16:26:55', '2020-12-06 16:26:55'),
(13, 2, 192, '2020-12-06', '16:27:01', '2020-12-06 16:27:01'),
(14, 1, 193, '2020-12-06', '17:32:16', '2020-12-06 17:32:16'),
(15, 1, 194, '2020-12-06', '17:32:22', '2020-12-06 17:32:22'),
(16, 1, 195, '2020-12-06', '17:32:28', '2020-12-06 17:32:28'),
(17, 1, 196, '2020-12-06', '17:32:35', '2020-12-06 17:32:35'),
(18, 1, 197, '2020-12-06', '17:32:41', '2020-12-06 17:32:41'),
(19, 1, 198, '2020-12-06', '17:32:47', '2020-12-06 17:32:47'),
(20, 1, 199, '2020-12-06', '17:32:53', '2020-12-06 17:32:53'),
(21, 1, 200, '2020-12-06', '17:32:59', '2020-12-06 17:32:59'),
(22, 1, 201, '2020-12-06', '17:35:05', '2020-12-06 17:35:05'),
(23, 1, 202, '2020-12-06', '17:35:17', '2020-12-06 17:35:17'),
(24, 1, 203, '2020-12-06', '17:35:23', '2020-12-06 17:35:23'),
(25, 1, 204, '2020-12-06', '17:35:29', '2020-12-06 17:35:29'),
(26, 1, 205, '2020-12-06', '17:35:41', '2020-12-06 17:35:41'),
(27, 1, 206, '2020-12-06', '17:43:05', '2020-12-06 17:43:05'),
(28, 3, 209, '2020-12-06', '17:43:11', '2020-12-06 17:43:11'),
(29, 2, 211, '2020-12-06', '17:43:17', '2020-12-06 17:43:17'),
(30, 2, 213, '2020-12-06', '17:43:23', '2020-12-06 17:43:23'),
(31, 2, 215, '2020-12-06', '17:43:29', '2020-12-06 17:43:29'),
(32, 3, 218, '2020-12-06', '17:43:36', '2020-12-06 17:43:36'),
(33, 2, 220, '2020-12-06', '17:43:42', '2020-12-06 17:43:42');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `watermeter`
--
ALTER TABLE `watermeter`
ADD PRIMARY KEY (`id`),
ADD KEY `dater` (`dater`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `watermeter`
--
ALTER TABLE `watermeter`
MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1061;
/*!40101 SET CHARACTER_SET_CLIENT=#OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=#OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=#OLD_COLLATION_CONNECTION */;
UPDATE 1.
Making little progress I think - Totals are not right yet.
SELECT '(a.dater, b.dater)', DATE_FORMAT(a.dater,'%H:%i') AS dater,
YEAR(a.dater),
MONTHNAME(a.dater),
DAY(a.dater),
HOUR(a.dater),
MINUTE(a.dater),
a.time,
SUM(a.liter) as liter
FROM watermeter a
INNER JOIN watermeter b
ON b.dater >= a.dater
WHERE b.dater <= DATE_ADD(a.dater, INTERVAL 60 SECOND)
AND a.date LIKE '2020-12-08' GROUP BY YEAR(a.date), MONTHNAME(a.date), DAY(a.dater), HOUR(a.dater), MINUTE(a.dater)
ORDER BY a.id ASC
Update2
So Update one does not give me the correct result. Tried now the following that I got from : MySQL GROUP BY DateTime +/- 3 seconds but also no joy yet.
SELECT COUNT(liter),DAY(dater),HOUR(dater),MINUTE(dater)
FROM watermeter
JOIN (SELECT watermeter.id, MAX(S.dater) AS ChainStartTime
FROM watermeter
JOIN (SELECT DISTINCT a.dater
FROM watermeter a
LEFT JOIN watermeter b
ON (b.dater >= a.dater - INTERVAL 60 SECOND
AND b.dater < a.dater)
WHERE b.dater IS NULL
AND a.date LIKE '2020-12-06') S
ON watermeter.dater >= S.dater
GROUP BY watermeter.id) GroupingQuery
ON watermeter.id = GroupingQuery.id
GROUP BY GroupingQuery.ChainStartTime
First find the difference in time from the previous row:
select
id,
liter,
total_liters,
dater,
lead(dater) over (order by dater) as "lead",
timediff(dater, lead(dater) over (order by dater)) as d1
from watermeter
order by dater;
output:
+----+-------+--------------+---------------------+---------------------+-----------+
| id | liter | total_liters | dater | lead | d1 |
+----+-------+--------------+---------------------+---------------------+-----------+
| 9 | 3 | 184 | 2020-12-06 16:14:58 | 2020-12-06 16:15:04 | -00:00:06 |
| 10 | 1 | 185 | 2020-12-06 16:15:04 | 2020-12-06 16:26:49 | -00:11:45 |
| 11 | 3 | 188 | 2020-12-06 16:26:49 | 2020-12-06 16:26:55 | -00:00:06 |
| 12 | 2 | 190 | 2020-12-06 16:26:55 | 2020-12-06 16:27:01 | -00:00:06 |
| 13 | 2 | 192 | 2020-12-06 16:27:01 | 2020-12-06 17:32:16 | -01:05:15 |
| 14 | 1 | 193 | 2020-12-06 17:32:16 | 2020-12-06 17:32:22 | -00:00:06 |
| 15 | 1 | 194 | 2020-12-06 17:32:22 | 2020-12-06 17:32:28 | -00:00:06 |
| 16 | 1 | 195 | 2020-12-06 17:32:28 | 2020-12-06 17:32:35 | -00:00:07 |
| 17 | 1 | 196 | 2020-12-06 17:32:35 | 2020-12-06 17:32:41 | -00:00:06 |
etc...
Then determine which times you would like to see, because they have a difference to their previous row which is larger than 60 seconds.
(The id of the column is show in x)
with cte as (
select id,
dater,
liter,
total_liters,
d1,
abs(time_to_sec(d1)) as g1,
case when abs(time_to_sec(d1))>60 then id else 0 end as x
from (
select
id,
liter,
total_liters,
dater,
lead(dater) over (order by dater) as "lead",
timediff(dater, lead(dater) over (order by dater)) as d1
from watermeter
order by dater
) tmp1
)
select * from cte;
output:
+----+---------------------+-------+--------------+-----------+------+----+
| id | dater | liter | total_liters | d1 | g1 | x |
+----+---------------------+-------+--------------+-----------+------+----+
| 9 | 2020-12-06 16:14:58 | 3 | 184 | -00:00:06 | 6 | 0 |
| 10 | 2020-12-06 16:15:04 | 1 | 185 | -00:11:45 | 705 | 10 |
| 11 | 2020-12-06 16:26:49 | 3 | 188 | -00:00:06 | 6 | 0 |
| 12 | 2020-12-06 16:26:55 | 2 | 190 | -00:00:06 | 6 | 0 |
| 13 | 2020-12-06 16:27:01 | 2 | 192 | -01:05:15 | 3915 | 13 |
| 14 | 2020-12-06 17:32:16 | 1 | 193 | -00:00:06 | 6 | 0 |
| 15 | 2020-12-06 17:32:22 | 1 | 194 | -00:00:06 | 6 | 0 |
| 16 | 2020-12-06 17:32:28 | 1 | 195 | -00:00:07 | 7 | 0 |
etc...
Next step is to determine the max(id) which 'belongs' to the x:
with cte as (
select id,
dater,
liter,
total_liters,
d1,
abs(time_to_sec(d1)) as g1,
case when abs(time_to_sec(d1))>60 then id else 0 end as x
from (
select
id,
liter,
total_liters,
dater,
lead(dater) over (order by dater) as "lead",
timediff(dater, lead(dater) over (order by dater)) as d1
from watermeter
order by dater
) tmp1
)
select
id,
dater,
liter,
total_liters
,d1,
g1,
x,
(select min(x)-1 from cte c2 where c2.id>c1.x and c2.x>0) as y
from cte c1
where c1.x<>0
;
output:
+----+---------------------+-------+--------------+-----------+------+----+------+
| id | dater | liter | total_liters | d1 | g1 | x | y |
+----+---------------------+-------+--------------+-----------+------+----+------+
| 10 | 2020-12-06 16:15:04 | 1 | 185 | -00:11:45 | 705 | 10 | 12 |
| 13 | 2020-12-06 16:27:01 | 2 | 192 | -01:05:15 | 3915 | 13 | 20 |
| 21 | 2020-12-06 17:32:59 | 1 | 200 | -00:02:06 | 126 | 21 | 25 |
| 26 | 2020-12-06 17:35:41 | 1 | 205 | -00:07:24 | 444 | 26 | NULL |
+----+---------------------+-------+--------------+-----------+------+----+------+
Note that x and y are the minimum and maximum id for your group.
Finally (this messy stuff):
with cte as (
select id,
dater,
liter,
total_liters,
d1,
abs(time_to_sec(d1)) as g1,
case when abs(time_to_sec(d1))>60 then id else 0 end as x
from (
select
id,
liter,
total_liters,
dater,
lead(dater) over (order by dater) as "lead",
timediff(dater, lead(dater) over (order by dater)) as d1
from watermeter
order by dater
) tmp1
)
select
id,
dater,
(select sum(liter) from watermeter where id between x and y) as rain
from (
select
id,
dater,
liter,
total_liters
,d1,
g1,
x,
(select min(x)-1 from cte c2 where c2.id>c1.x and c2.x>0) as y
from cte c1
where c1.x<>0
) tmp2
;
gives output:
+------+---------------------+------+
| id | dater | rain |
+------+---------------------+------+
| 10 | 2020-12-06 16:15:04 | 6 |
| 13 | 2020-12-06 16:27:01 | 9 |
| 21 | 2020-12-06 17:32:59 | 5 |
| 26 | 2020-12-06 17:35:41 | NULL |
+------+---------------------+------+
I do hope this is close to the expected output...
With the help of Luuk's code and learning a lot about "case" and "lag" and lead" and using nested selects etc. I was able to get a working query for what I wanted.
SET #wgroup := 0;
with cte as (
select
id,
dater,
liter,
total_liters,
d1,
abs(time_to_sec(d1)) as g1,
case when abs(time_to_sec(d1))>60 then #wgroup := #wgroup+1 else #wgroup end as wgroup
from (
select
id,
liter,
total_liters,
dater,
(case
WHEN lag(dater) over (order by dater) IS NULL
THEN timediff(dater, lead(dater) over (order by dater))
ELSE timediff(dater, lag(dater) over (order by dater))
END) AS d1
from watermeter where date like '2020-12-06'
order by dater
) tmp1
)
(select dater,
wgroup,
SUM(liter)
from cte
GROUP BY wgroup)
;
With this I was able to sum all values where the time difference is less than 60 second from without loosing a single line.
+---------------------+--------+------------+
| dater | wgroup | SUM(liter) |
+---------------------+--------+------------+
| 2020-12-06 16:14:58 | 0 | 4 |
| 2020-12-06 16:26:49 | 1 | 7 |
| 2020-12-06 17:32:16 | 2 | 8 |
| 2020-12-06 17:35:05 | 3 | 5 |
| 2020-12-06 17:43:05 | 4 | 308 |
| 2020-12-06 19:19:03 | 5 | 120 |
| 2020-12-06 19:31:29 | 6 | 4 |
| 2020-12-06 19:34:48 | 7 | 1 |
| 2020-12-06 20:30:08 | 8 | 1 |
| 2020-12-06 21:27:06 | 9 | 23 |
+---------------------+--------+------------+
Related
Tried many suggestions to get this to work, difficult to explain so below is the data I have and the result I want to achieve.
I want to update the 'Active' Column to 0 if its not the MAX Ldate.
ID | SNumber | Ldate | Active
4804 188 2015-11-17 1
4806 189 2015-11-25 1
4807 190 2015-11-25 1
4808 191 2015-11-19 1
4809 192 2015-11-19 1
4820 193 2015-11-17 1
4821 193 2016-06-08 1
4830 194 2015-11-17 1
4831 194 2016-06-08 1
4828 195 2015-11-17 1
4829 195 2016-06-08 1
ID SNumber Ldate Active
4804 188 2015-11-17 1
4806 189 2015-11-25 1
4807 190 2015-11-25 1
4808 191 2015-11-19 1
4809 192 2015-11-19 1
4820 193 2015-11-17 0
4821 193 2016-06-08 1
4830 194 2015-11-17 0
4831 194 2016-06-08 1
4828 195 2015-11-17 0
4829 195 2016-06-08 1
I can get all rows with the MAX Ldate by "select ID, SNumber, Ldate from (select * from tbl order by SNumber, Ldate desc) x group by SNumber"
Thanks for taking the time to look!
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,SNumber INT NOT NULL
,Ldate DATE NOT NULL
);
INSERT INTO my_table VALUES
(4804,188,'2015-11-17'),
(4806,189,'2015-11-25'),
(4807,190,'2015-11-25'),
(4808,191,'2015-11-19'),
(4809,192,'2015-11-19'),
(4820,193,'2015-11-17'),
(4821,193,'2016-06-08'),
(4830,194,'2015-11-17'),
(4831,194,'2016-06-08'),
(4828,195,'2015-11-17'),
(4829,195,'2016-06-08');
SELECT x.*
, COALESCE(x.ldate = y.ldate,0) active
FROM my_table x
LEFT
JOIN
( SELECT snumber
, MAX(ldate) ldate
FROM my_table
GROUP
BY snumber
) y
ON y.snumber = x.snumber
AND y.ldate = x.ldate;
+------+---------+------------+--------+
| id | SNumber | Ldate | active |
+------+---------+------------+--------+
| 4804 | 188 | 2015-11-17 | 1 |
| 4806 | 189 | 2015-11-25 | 1 |
| 4807 | 190 | 2015-11-25 | 1 |
| 4808 | 191 | 2015-11-19 | 1 |
| 4809 | 192 | 2015-11-19 | 1 |
| 4820 | 193 | 2015-11-17 | 0 |
| 4821 | 193 | 2016-06-08 | 1 |
| 4830 | 194 | 2015-11-17 | 0 |
| 4831 | 194 | 2016-06-08 | 1 |
| 4828 | 195 | 2015-11-17 | 0 |
| 4829 | 195 | 2016-06-08 | 1 |
+------+---------+------------+--------+
I can't think why you'd store this, but it's easy enough to change the above to an UPDATE. It might look something like this (obviously, you'd need to alter the table design above first)...
UPDATE my_table x
LEFT
JOIN
( SELECT snumber
, MAX(ldate) ldate
FROM my_table
GROUP
BY snumber
) y
ON y.snumber = x.snumber
AND y.ldate = x.ldate
SET x.active = 0
WHERE y.snumber IS NULL;
But I think I would normally use an INNER JOIN for an UPDATE, in which case it might look like this (perhaps bound up inside a transaction)...
UPDATE my_table SET active = 0;
UPDATE my_table x
JOIN
( SELECT snumber
, MAX(ldate) ldate
FROM my_table
GROUP
BY snumber
) y
ON y.snumber = x.snumber
AND y.ldate = x.ldate
SET x.active = 1;
I have two columns coming from my sql query- month, value i.e. values are coming monthwise. My requirement is to club these months in the group of 3 months wise...and the values should come the average of these 3.
Ex.I have following data-
Month Values
Mar-14 50
Apr-14 51
May-14 52
Jun-14 53
Jul-14 54
Aug-14 55
Sep-14 56
Oct-14 57
Nov-14 58
Dec-14 59
Jan-15 60
Feb-15 61
Mar-15 62
Apr-15 63
May-15 64
Jun-15 65
Jul-15 66
Aug-15 67
Sep-15 68
Oct-15 69
Nov-15 70
Dec-15 71
Jan-16 72
Feb-16 73
Mar-16 74
Apr-16 75
May-16 76
Jun-16 77
Jul-16 78
Aug-16 79
Sep-16 80
Oct-16 81
Nov-16 82
Dec-16 83
Jan-17 84
Feb-17 85
Mar-17 86
How can I achieve following output in MySql-
3 Months Clubing Avg of Values
Mar-14 51
Jun-14 54
Sep-14 57
Dec-14 60
Mar-15 63
Jun-15 66
Sep-15 69
Dec-15 72
Mar-16 75
Jun-16 78
Sep-16 81
Thanks in Advance
A bit messy but you could use variables -assuming you have an incrementing id column (or soemthing you can order by)
drop table if exists t;
create table t(id int auto_increment primary key,Month varchar(10), Valus int);
insert into t (month,valus) values
('Mar-14', 50),
('Apr-14', 51),
('May-14', 52),
('Jun-14', 53),
('Jul-14', 54),
('Aug-14', 55),
('Sep-14', 56),
('Oct-14', 57),
('Nov-14', 58),
('Dec-14', 59);
select id,mth,rt
from
(
select id,month,valus,
#count:=#count+1 counter,
if(#count=1,#mth:=month,#mth:=#mth) mth,
if(#count=1,#block:=#block+1,#block:=#block) block,
if(#count<3,#sum:=#sum+valus,#sum:=(#sum+valus) / 3) rt,
if(#count=3,#count:=0,#count:=#count) creset,
if(#count=0,#sum:=0,#sum:=#sum) sumreset
from t
cross join (select #m ='',#count:=0,#sum:=0,#block:=0,#mth:='') s
order by id
)t
where counter = 3;
+----+--------+------+
| id | mth | rt |
+----+--------+------+
| 3 | Mar-14 | 51 |
| 6 | Jun-14 | 54 |
| 9 | Sep-14 | 57 |
+----+--------+------+
3 rows in set (0.03 sec)
Slightly less messy but using sql's avg function and using variables to fill down the first month in a 3 month block
select block,mth,avg(valus)
from
(
select id,month,valus,
#count:=#count+1 counter,
if(#count=1,#mth:=month,#mth:=#mth) mth,
if(#count=1,#block:=#block+1,#block:=#block) block,
if(#count=3,#count:=0,#count:=#count) creset
from t
cross join (select #block:=0,#count:=0,#mth:='') s
order by id
) t
group by block,mth
order by block,mth
+-------+--------+------------+
| block | mth | avg(valus) |
+-------+--------+------------+
| 1 | Mar-14 | 51.0000 |
| 2 | Jun-14 | 54.0000 |
| 3 | Sep-14 | 57.0000 |
| 4 | Dec-14 | 59.0000 |
+-------+--------+------------+
4 rows in set (0.05 sec)
Try this
create temporary table tab (month1 varchar(30), id int);
insert into tab (month1,id)
values('Mar-14' ,50),
('Apr-14' ,51),
('May-14' ,52),
('Jun-14' ,53),
('Jul-14' ,54),
('Aug-14' ,55),
('Sep-14' ,56),
('Oct-14' ,57),
('Nov-14' ,58),
('Dec-14' ,59),
('Jan-15' ,60),
('Feb-15' ,61),
('Mar-14' ,62);
set #row_number = 0;
select *
from tab where (#row_number := #row_number+1)%3= 1;
Result
month1 id
'Mar-14' '50'
'Jun-14' '53'
'Sep-14' '56'
'Dec-14' '59'
'Mar-14' '62'
I found tons of examples in Stackoverflow about how to select first and last row in a group but I cannot adapt them to my need. Alas my limited knowledge of MySQL doesn't help.
Some data (date_time, val1 and val2) are saved in random moments. I need to group them for 15 minutes and then calculate, for each 15 min interval:
number of row in the interval (done)
val1 min (done)
val1 avg (done)
val1 max (done)
first val2 in the group (done, easy with COALESCE)
last val2 in the group (here I need your help)
Here's my data, desired result, and best effort to date...
# what I would like to obtain:
#_timeslice _count _min_val1 _avg_val1 _max_val1 _first_val2 _last_val2
# 2017-11-01T00:00:00Z 4 90 100 110 200 210
# 2017-11-01T00:15:00Z 3 100 110 120 240 230
# 2017-11-01T00:30:00Z 2 110 120 130 270 265
# 2017-11-01T00:45:00Z 4 80 112.5 150 290 320
CREATE TABLE `test-table`
(`date_time` datetime, `val1` float, `val2` float);
INSERT INTO `test-table`
(`date_time`, `val1`, `val2`)
VALUES
('2017-11-01 00:00:00', 100, 200), # first 15 min
('2017-11-01 00:01:00', 110, 190),
('2017-11-01 00:02:05', 90, 220),
('2017-11-01 00:14:00', 100, 210),
('2017-11-01 00:15:00', 100, 240), # second 15 min
('2017-11-01 00:16:00', 110, 250),
('2017-11-01 00:28:00', 120, 230),
('2017-11-01 00:30:00', 110, 270), # third 15 min
('2017-11-01 00:44:59', 130, 265),
('2017-11-01 00:50:00', 120, 290), # fourth 15 min
('2017-11-01 00:55:00', 150, 300),
('2017-11-01 00:57:00', 100, 280),
('2017-11-01 00:58:00', 80, 320)
;
SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`date_time`)/900)*900) AS _timeslice,
COUNT(*) AS _count,
min(`val1`) as _min_val1,
avg(`val1`) as _avg_val1,
max(`val1`) as _max_val1,
coalesce(`val2`) as _first_val2 # returns the first val2 in the group
# ----> how to add here the last val2 for the group?
FROM `test-table`
GROUP BY _timeslice;
...and here's a fiddle of same SQLFiddle_grouping_15_min
Can somebody help me with the syntax to use?
SELECT a.*
FROM `test-table` a
JOIN
( SELECT FLOOR(UNIX_TIMESTAMP(date_time)/900) timeslice
, MIN(date_time) min_date_time
, MAX(date_time) max_date_time
FROM `test-table`
GROUP
BY timeslice
) b
ON b.timeslice = FLOOR(UNIX_TIMESTAMP(a.date_time)/900)
AND a.date_time IN(b.min_date_time,b.max_date_time);
+---------------------+------+------+
| date_time | val1 | val2 |
+---------------------+------+------+
| 2017-11-01 00:00:00 | 100 | 200 |
| 2017-11-01 00:14:00 | 100 | 210 |
| 2017-11-01 00:15:00 | 100 | 240 |
| 2017-11-01 00:28:00 | 120 | 230 |
| 2017-11-01 00:30:00 | 110 | 270 |
| 2017-11-01 00:44:59 | 130 | 265 |
| 2017-11-01 00:50:00 | 120 | 290 |
| 2017-11-01 00:58:00 | 80 | 320 |
+---------------------+------+------+
I found a simple solution with a straightforward syntax. It is possibly the easiest for a newbie like me:
SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`date_time`)/900)*900) AS _timeslice,
COUNT(*) AS _count,
min(`val1`) as _min_val1,
avg(`val1`) as _avg_val1,
max(`val1`) as _max_val1,
SUBSTRING_INDEX(GROUP_CONCAT(CAST(`val2` AS CHAR) ORDER BY date_time), ',', 1) AS _first_val2,
SUBSTRING_INDEX(GROUP_CONCAT(CAST(`val2` AS CHAR) ORDER BY date_time DESC), ',', 1) AS _last_val2
FROM `test-table`
GROUP BY _timeslice;
Fiddle at SQLFiddle_solved
I have this table named 'Values' in a MySQL database:
sp_id W Value_C Top_C
742 11 11.42 1.15
742 12 114.35 2.44
742 13 27.2 0.42
742 14 55.38 6.2
742 15 23.77 16.7
15 11 10.59 9.15
15 12 12.4 12.3
15 13 157.89 0.72
15 14 51.2 1.5
15 15 11.78 0.51
117 11 27.48 0.92
117 12 21.416 0.76
117 13 75.31 0.39
117 14 2.182 0.79
252 11 15.54 0.25
252 12 10.209 0.32
252 13 14.41 0.44
252 14 68.34 2.16
I am looking to rearrange the data. I am looking to get this:
sp_id 11 11 12 12 13 13 14 14 15 15
742 11.42 1.15 114.35 2.44 27.2 0.42 55.38 6.2 23.77 6.77
15 10.59 9.15 12.4 12.3 157.89 0.72 51.2 1.5 11.78 0.51
117 27.48 0.92 21.416 0.76 75.31 0.39 2.182 0.79 NULL NULL
252 15.54 0.25 10.209 0.32 14.41 0.44 68.34 2.16 NULL NULL
I can get them as columns, but I am having difficulty getting them as rows. When I use CASE, I am getting NULLs in many places.
How could I SELECT this subset with a MySQL query?
EDIT: Corrected the title.
You can try something like this if the W values are limited :
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE ValuesTable
(`sp_id` int, `W` int, `Value_C` decimal(10,2), `Top_C` decimal(10,2))
;
INSERT INTO ValuesTable
(`sp_id`, `W`, `Value_C`, `Top_C`)
VALUES
(742, 11, 11.42, 1.15),
(742, 12, 114.35, 2.44),
(742, 13, 27.2, 0.42),
(742, 14, 55.38, 6.2),
(742, 15, 23.77, 16.7),
(15, 11, 10.59, 9.15),
(15, 12, 12.4, 12.3),
(15, 13, 157.89, 0.72),
(15, 14, 51.2, 1.5),
(15, 15, 11.78, 0.51),
(117, 11, 27.48, 0.92),
(117, 12, 21.416, 0.76),
(117, 13, 75.31, 0.39),
(117, 14, 2.182, 0.79),
(252, 11, 15.54, 0.25),
(252, 12, 10.209, 0.32),
(252, 13, 14.41, 0.44),
(252, 14, 68.34, 2.16)
;
Query 1:
SELECT DISTINCT VT.sp_id,
VT11.Value_C as 11v, VT11.Top_C as 11t,
VT12.Value_C as 12v, VT12.Top_C as 12t,
VT13.Value_C as 13v, VT13.Top_C as 13t,
VT14.Value_C as 14v, VT14.Top_C as 14t,
VT15.Value_C as 15v, VT15.Top_C as 15t
FROM ValuesTable VT
LEFT OUTER JOIN ValuesTable VT11 ON VT.sp_id = VT11.sp_id AND VT11.W = 11
LEFT OUTER JOIN ValuesTable VT12 ON VT.sp_id = VT12.sp_id AND VT12.W = 12
LEFT OUTER JOIN ValuesTable VT13 ON VT.sp_id = VT13.sp_id AND VT13.W = 13
LEFT OUTER JOIN ValuesTable VT14 ON VT.sp_id = VT14.sp_id AND VT14.W = 14
LEFT OUTER JOIN ValuesTable VT15 ON VT.sp_id = VT15.sp_id AND VT15.W = 15
Results:
| SP_ID | 11V | 11T | 12V | 12T | 13V | 13T | 14V | 14T | 15V | 15T |
|-------|-------|------|--------|------|--------|------|-------|------|--------|--------|
| 742 | 11.42 | 1.15 | 114.35 | 2.44 | 27.2 | 0.42 | 55.38 | 6.2 | 23.77 | 16.7 |
| 15 | 10.59 | 9.15 | 12.4 | 12.3 | 157.89 | 0.72 | 51.2 | 1.5 | 11.78 | 0.51 |
| 117 | 27.48 | 0.92 | 21.42 | 0.76 | 75.31 | 0.39 | 2.18 | 0.79 | (null) | (null) |
| 252 | 15.54 | 0.25 | 10.21 | 0.32 | 14.41 | 0.44 | 68.34 | 2.16 | (null) | (null) |
I am facing issue in finding the data from MySql table.
Table A:
+-------+-------------+------+-----+-------------------+-------+
| ID | Table_b_fk |Value | age | name | score |
+-------+-------------+------+-----+-------------------+-------+
| 01 | 01 | 255 | 21 | Tom | 65 |
| 02 | 02 | 36 | 20 | Peter | 95 |
| 03 | 03 | 25 | 22 | John | 65 |
| 04 | 04 | 36 | 20 | Bond | 95 |<<----First
| 05 | 05 | 258 | 22 | Smith | 65 |
| 06 | 06 | 420 | 20 | Robert | 95 |
| 07 | 07 | 258 | 22 | Nisarg Patel | 65 |
| 08 | 08 | 16 | 21 | Peter | 95 |
| 09 | 09 | 25 | 23 | J0k | 65 |
| 10 | 10 | 36 | 22 | Prodigitalson | 95 |
| 11 | 11 | 205 | 22 | Silver | 65 |<<----Next
| 12 | 12 | 37 | 20 | Json | 95 |
| 13 | 13 | 285 | 23 | Villa | 65 |
| 14 | 14 | 36 | 22 | Parker | 95 |
+-------+-------------+------+-----+-------------------+-------+
Table B:
+-------+-------------+------+-----+-------------------+-------+
| ID | Result | M1 | M2 | name | score |
+-------+-------------+------+-----+-------------------+-------+
| 01 | Pass | 30 | 26 | Tom | 65 |
| 02 | Pass | 30 | 20 | Peter | 95 |
| 03 | Pass | 25 | 60 | John | 65 |
| 04 | Pass | 100 | 100 | Bond | 95 |<<----First
| 05 | Pass | 55 | 65 | Smith | 65 |
| 06 | Pass | 80 | 95 | Robert | 95 |
| 07 | Pass | 65 | 75 | Nisarg Patel | 65 |
| 08 | Pass | 56 | 71 | Peter | 95 |
| 09 | Pass | 90 | 96 | J0k | 65 |
| 10 | Pass | 96 | 96 | Prodigitalson | 95 |
| 11 | Pass | 100 | 100 | Silver | 65 |<<----Next
| 12 | Pass | 47 | 92 | Json | 95 |
| 13 | Pass | 82 | 73 | Villa | 65 |
| 14 | Pass | 86 | 72 | Parker | 95 |
+-------+-------------+------+-----+-------------------+-------+
I am joining TableA & TableB, where in TableA Table_b_fk is foreign key to TableB.
I am finding the record which matches the TableB column M1 & M2 = 100.
My Scenario: 1
I know the first occurrence of the match record ID : 04 in TableA. I want to do a search to find the next record with M1 & M2 = 100. (Record Id-11) But the search should not start from 01. It should start from the last found record Id. That is from O4 the search should start to find the next occurrence of the record.
My Try:
I tried to find using Limit but it didn't help me to find. Can some one help me in this?
Edit: 1
My Scenario: 2
In my second case my TableB has repeated Data and the ID was foreign in TableA. How can I fins the record. ? with the matching ID/M1/M2 values: I found a solution for that. I just want to find the Current Record FOREIGN KEY and Check for the next occurrence of the record in the same table and I can get the next record rite?
In this case my TableB record are not as same as TableA records. In other words my TableA records will point to tableA. Many-to-one. Is this rite?
Edit: 2
Thanks for all your efforts and knowledge I found a solution for scenario:2 check it:
CREATE TABLE TableB (
ID Int,
Result VARCHAR(20),
M1 INT,
M2 INT,
name VARCHAR(20),
Score INT);
INSERT INTO TableB VALUES
( 11 , 'Pass' , 30 , 26 , 'Tom' , 65 ),
( 13 , 'Pass' , 30 , 20 , 'Peter' , 95 ),
( 80 , 'Pass' , 25 , 60 , 'John' , 65 ),
( 81 , 'Pass' , 100 , 100 , 'Bond' , 95 ),
( 90 , 'Pass' , 55 , 65 , 'Smith' , 65 ),
( 96 , 'Pass' , 80 , 95 , 'Robert' , 95 ),
( 97 , 'Pass' , 65 , 75 , 'Nisarg Patel' , 65 ),
( 98 , 'Pass' , 56 , 71 , 'Peter' , 95 ),
( 99 , 'Pass' , 90 , 96 , 'J0k' , 65 ),
( 100 , 'Pass' , 96 , 96 , 'Prodigitalson' , 95 ),
( 101 , 'Pass' , 10 , 10 , 'Silver' , 65 ),
( 103 , 'Pass' , 47 , 92 , 'Json' , 95 ),
( 201 , 'Pass' , 82 , 73 , 'Villa' , 65 ),
( 222 , 'Pass' , 86 , 72 , 'Parker' , 95 )
;
CREATE TABLE TableA
(`ID` int, `Table_b_fk` int, `Value` int, `age` int, `name` varchar(13), `score` int)
;
INSERT INTO TableA
(`ID`, `Table_b_fk`, `Value`, `age`, `name`, `score`)
VALUES
(01, 11, 255, 21, 'Tom', 65),
(02, 81, 36, 20, 'Peter', 95),
(03, 80, 25, 22, 'John', 65),
(04, 97, 36, 20, 'Bond', 95),
(05, 81, 258, 22, 'Smith', 65),
(06, 06, 420, 20, 'Robert', 95),
(07, 81, 258, 22, 'Nisarg Patel', 65),
(08, 08, 16, 21, 'Peter', 95),
(09, 96, 25, 23, 'J0k', 65),
(10, 101, 36, 22, 'Prodigitalson', 95),
(11, 222, 205, 22, 'Silver', 65),
(12, 12, 37, 20, 'Json', 95),
(13, 201, 285, 23, 'Villa', 65),
(14, 101, 36, 22, 'Parker', 95)
;
Solution for that is:
SELECT a.id
FROM TableB b
INNER JOIN TableA a
ON a.Table_b_fk = b.id
WHERE M1 = 100 and M2 = 100 AND a.ID>4 limit 1
where the limit just limits the next record.. (answer is 5).
I case of Doctrine 2: Use the below Query code.
$qry = $this->manager()->createQueryBuilder()
->select(array('e', 's'))
->from('YOUR_DOMAIN', 'e')
->Join('e.table_b_k', 's')
->where("s.m1 = ?", $valueone)
->andwhere("s.m2 = ?", $valuetwo)
->andwhere("e.id > ?", $currentrecord)
->setMaxResult(1);
Note: YOUR_DOMAIN here is the TableA. TableA and TableB should be joined through the Mapping so we dont need to Join/Reference int he Query.. directly as TableB. The will be done by the second line Join in the above example. It is not tested as of now.
What about this give it a try
SELECT b.id AS next_id FROM tableb b LEFT JOIN tablea a
ON (b.id =a.Table_b_fk ) WHERE b.M1=100 AND b.M2 =100 AND b.id >4
ORDER BY b.id ASC LIMIT 1
It gives the next occurrence of *M1 =100 and M2 =100 *
See Fiddle Example it returns 11 the next occurrence *M1 =100 and M2 =100 *
If I understand your question correctly, I think you are looking for this:
SET #id:=4;
SELECT *
FROM TableA
WHERE Table_b_fk =
(SELECT MIN(ID)
FROM TableB
WHERE (M1,M2) = (SELECT M1, M2 FROM TableB WHERE ID=#id)
AND ID>#id)
This query will select the second row from TableA that has M1=100 and M2=100 in the second table.
try this
SELECT a.id
FROM TableB b
INNER JOIN TableA a
ON a.Table_b_fk = b.id
WHERE M1 = 100 and M2 = 100 AND b.ID>4
output:
ID
11
demo FIDLLE
I am finding the record which matches the TableB column M1 & M2 = 100.
Assuming you mean:
I am finding the tableA record which matches the TableB column M1 = 100 AND M2 = 100.
SELECT *
FROM table_a a
WHERE EXISTS (
SELECT *
FROM table_b b
WHERE b.id = a.tableb_fk
AND b.m1 = 100 AND b.m2 = 100
);
UPDATE: since the OP appears to want to suppress the first matching record from the result (I assume: the one with the lowest id), one could add an extra EXIST to the WHERE clause to suppress the first match:
SELECT *
FROM table_a a
WHERE EXISTS (
SELECT *
FROM table_b b
WHERE b.id = a.tableb_fk
AND b.m1 = 100 AND b.m2 = 100
AND EXISTS (
SELECT *
FROM table_b bb
WHERE bb.id < b.id
AND bb.m1 = 100 AND bb.m2 = 100
)
);