SQL Select Query Help. Maximum sum of consequtive four rows. - mysql

We have a traffic counter that counts cars in each lane (two inbound and two outbound) in 15 minute increments.
There is a peak period which is defined as 7:00am to 9:00am. Within this peak period we want to know the PeakHourIn and PeakHourOut and PeakHourSum.
The PeakHourIn is the highest consecutive 4x15 minute total (1 hour) for lne1in + lne4in
The PeakHourOut is the highest consecutive 4x15 minute total (1 hour) for lne2out + lne3out
The PeakHourSum is the highest consecutive 4x15 minute total (1 hour) for all lanes.
Date Time lne1in lne2out lne3out lne4in
09-18-2012 5:45 AM 2 0 0 0
09-18-2012 6:00 AM 1 0 0 1
09-18-2012 6:15 AM 2 1 0 0
09-18-2012 6:30 AM 2 1 0 0
09-18-2012 6:45 AM 6 1 2 1
09-18-2012 7:00 AM 9 1 0 3
09-18-2012 7:15 AM 81 12 22 15
09-18-2012 7:30 AM 144 31 63 56
09-18-2012 7:45 AM 84 30 62 42
09-18-2012 8:00 AM 7 1 0 3
09-18-2012 8:15 AM 11 2 3 3
09-18-2012 8:30 AM 12 3 7 1
09-18-2012 8:45 AM 16 4 8 0
09-18-2012 9:00 AM 5 2 5 0
09-18-2012 9:15 AM 10 1 4 0
Results should look like:
PeakHourIn 434
PeakHourOut 221
PeakHourSum 655
Any help would be greatly appreciated.

If you used a native temporal data type to store the date/time, you could group multiple self-joins:
SELECT MAX(lne1in + lne4in ) AS PeakHourIn,
MAX(lne2out + lne3out) AS PeakHourOut,
MAX(lne1in + lne2out + lne3out + lne4in) AS PeakHourSum
FROM (
SELECT t1.lne1in + t2.lne1in + t3.lne1in + t4.lne1in AS lne1in,
t1.lne2out + t2.lne2out + t3.lne2out + t4.lne2out AS lne2out,
t1.lne3out + t2.lne3out + t3.lne3out + t4.lne3out AS lne3out,
t1.lne4in + t2.lne4in + t3.lne4in + t4.lne4in AS lne4in
FROM my_table t1
JOIN my_table t2 ON t2.DateTime = t1.DateTime + INTERVAL 15 MINUTE
JOIN my_table t3 ON t3.DateTime = t2.DateTime + INTERVAL 15 MINUTE
JOIN my_table t4 ON t4.DateTime = t3.DateTime + INTERVAL 15 MINUTE
WHERE TIME(t1.DateTime) BETWEEN '07:00:00' AND '08:00:00'
GROUP BY t1.DateTime
) t

EDIT
Here's a solution in MySQL: http://sqlfiddle.com/#!2/ff0fb/9
create table TrafficData
(
StartTime timestamp
,Lane int
,CarCount int
);
create table LaneData
(
Lane int
, Direction bit
);
insert LaneData
select 1, 0
union select 2, 1
union select 3, 1
union select 4, 0;
insert TrafficData
select dt, lane
, case lane
when 1 then l1
when 2 then l2
when 3 then l3
when 4 then l4
else null
end
from
(
select '2012-09-18 05:45' dt, 2 l1, 0 l2, 0 l3, 0 l4
union all select '2012-09-18 06:00', 1, 0, 0, 1
union all select '2012-09-18 06:15', 2, 1, 0, 0
union all select '2012-09-18 06:30', 2, 1, 0, 0
union all select '2012-09-18 06:45', 6, 1, 2, 1
union all select '2012-09-18 07:00', 9, 1, 0, 3
union all select '2012-09-18 07:15', 81, 12, 22, 15
union all select '2012-09-18 07:30', 144, 31, 63, 56
union all select '2012-09-18 07:45', 84, 30, 62, 42
union all select '2012-09-18 08:00', 7, 1, 0, 3
union all select '2012-09-18 08:15', 11, 2, 3, 3
union all select '2012-09-18 08:30', 12, 3, 7, 1
union all select '2012-09-18 08:45', 16, 4, 8, 0
union all select '2012-09-18 09:00', 5, 2, 5, 0
union all select '2012-09-18 09:15', 10, 1, 4, 0
) as originalTable
cross join LaneData;
select Lane, max(SumCarCount) as MaxSumCarCount
from
(
select a.Lane, SUM(b.CarCount) as SumCarCount
from TrafficData a
inner join TrafficData b
on b.Lane = a.Lane
and b.StartTime between a.StartTime and DATE_ADD(DATE_ADD(a.starttime, interval 1 hour), interval -1 second)
where time(a.StartTime) between '07:00' and '08:15'
group by a.Lane, a.StartTime
) x
group by Lane
order by Lane;
select Direction, max(SumCarCount) as MaxSumCarCount
from
(
select al.Direction, SUM(b.CarCount) SumCarCount
from TrafficData a
inner join LaneData al
on al.Lane = a.Lane
inner join TrafficData b
on b.StartTime between a.StartTime and DATE_ADD(DATE_ADD(a.starttime, interval 1 hour), interval -1 second)
inner join LaneData bl
on bl.Lane = b.Lane
and bl.Direction = al.Direction
where time(a.StartTime) between '07:00' and '08:15'
group by al.Direction, a.StartTime
) x
group by Direction
order by Direction;
ORIGINAL
Here's how I'd go about it in SQL Server:
--I'd change your table structure to be like this - that way you can easily add new lanes without rewriting the whole system
declare #trafficData table
(
StartTime DateTime
,Lane int
,CarCount int
)
--here's where you store additional info about the lanes (e.g. what direction they go in)
declare #laneData table
(
Lane int
, Direction bit --0 in, 1 out
)
--populate the tables with sample data
insert #laneData
select 1, 0
union select 2, 1
union select 3, 1
union select 4, 0
insert #trafficData
select dt, lane
, case lane
when 1 then l1
when 2 then l2
when 3 then l3
when 4 then l4
else null --should never happen
end
from
(
select '2012-09-18 5:45 AM' dt, 2 l1, 0 l2, 0 l3, 0 l4
union all select '2012-09-18 6:00 AM', 1, 0, 0, 1
union all select '2012-09-18 6:15 AM', 2, 1, 0, 0
union all select '2012-09-18 6:30 AM', 2, 1, 0, 0
union all select '2012-09-18 6:45 AM', 6, 1, 2, 1
union all select '2012-09-18 7:00 AM', 9, 1, 0, 3
union all select '2012-09-18 7:15 AM', 81, 12, 22, 15
union all select '2012-09-18 7:30 AM', 144, 31, 63, 56
union all select '2012-09-18 7:45 AM', 84, 30, 62, 42
union all select '2012-09-18 8:00 AM', 7, 1, 0, 3
union all select '2012-09-18 8:15 AM', 11, 2, 3, 3
union all select '2012-09-18 8:30 AM', 12, 3, 7, 1
union all select '2012-09-18 8:45 AM', 16, 4, 8, 0
union all select '2012-09-18 9:00 AM', 5, 2, 5, 0
union all select '2012-09-18 9:15 AM', 10, 1, 4, 0
) originalTable
cross join #laneData
--peak for each individual lane
select *
from
(
select a.Lane, a.StartTime, SUM(b.CarCount) SumCarCount
, ROW_NUMBER() over (partition by a.lane order by SUM(b.CarCount) desc) r
from #trafficData a
inner join #trafficData b
on b.Lane = a.Lane
and b.StartTime between a.StartTime and DATEADD(second,-1,DATEADD(hour,1,a.starttime))
group by a.Lane, a.StartTime
) x
where r = 1
order by Lane
--peak for lane direction
select *
from
(
select al.Direction, a.StartTime, SUM(b.CarCount) SumCarCount
, ROW_NUMBER() over (partition by al.Direction order by SUM(b.CarCount) desc) r
from #trafficData a
inner join #laneData al
on al.Lane = a.Lane
inner join #trafficData b
on b.StartTime between a.StartTime and DATEADD(second,-1,DATEADD(hour,1,a.starttime))
inner join #laneData bl
on bl.Lane = b.Lane
and bl.Direction = al.Direction
group by al.Direction, a.StartTime
) x
where r = 1
order by Direction

Related

Mysql select query count until reach the condition with condition

I have lists of users with his points and game id. I need to find the rank of the specified user based on the game order by the max(lb_point).
I have already done the query for getting the rank based on individual game as follows.
select count(*) AS user_rank
from (
select distinct user_id
from leader_board
where lb_point >= (select max( lb_point )
from leader_board
where user_id = 1
and game_id = 2 )
and game_id = 2
) t
But i need to find the rank based on the overall game. Example i have 3 different games (1,2,3). By passing the user_id, i need to find his overall rank among all three games. Can you please help me on this?
lb_id user_id game_id lb_point
------------------------------------------------
1 1 2 670
2 1 1 200
3 1 2 650
4 1 1 400
5 3 2 700
6 4 2 450
7 2 1 550
8 2 1 100
9 1 1 200
10 2 1 100
11 1 1 200
12 2 1 100
13 1 1 200
14 2 1 100
15 1 1 200
16 2 1 100
17 1 1 200
18 2 1 100
19 1 1 200
20 2 1 100
21 1 1 200
22 2 1 800
use sandbox;
/*create table t (lb_id int, user_id int, game_id int, lb_point int);
truncate table t;
insert into t values
(1 , 1, 2, 670),
(2 , 1, 1, 200),
(3 , 1, 2, 650),
(4 , 1, 1, 400),
(5 , 3, 2, 700),
(6 , 4, 2, 450),
(7 , 2, 1, 550),
(8 , 2, 1, 100),
(9 , 1, 1, 200),
(10, 2, 1, 100),
(11, 1, 1, 200),
(12, 2, 1, 100),
(13, 1, 1, 200),
(14, 2, 1, 100),
(15, 1, 1, 200),
(16, 2, 1, 100),
(17, 1, 1, 200),
(18, 2, 1, 100),
(19, 1, 1, 200),
(20, 2, 1, 100),
(21, 1, 1, 200),
(22, 2, 1, 800);
*/
select t.*
from
(
select s.*,#rn:=#rn+1 as rank
from
(
select user_id, sum(lb_point) points
from t
where lb_id = (select t1.lb_id from t t1 where t1.user_id = t.user_id and t1.game_id = t.game_id order by t1.lb_point desc limit 1)
group by user_id
order by points desc
) s
,(select #rn:=0) rn
) t
where t.user_id = 1
The innermost query grabs the highest score per game per user and sums it.
The next query assigns a rank based on the aggregated score per user.
The outermost query selects the user.

cumulative totals based on condition

I am trying to get the cumulative totals based on some criteria. Below is a dummy sample data set. I would like to get the cumulative time based on the Indicator as well as ID. When the Indicator is continuously 1 for the same ID, I would like to get the sum of all the Duration. If it becomes 0 then I would like to restart.
ID Duration Indicator Cumm_duration
1 30 0 30
1 30 1 60
1 30 0 30
1 30 0 30
1 30 1 60
1 30 0 30
1 30 0 30
1 30 0 30
1 30 0 30
1 30 0 30
1 30 0 30
1 30 0 30
1 30 1 60
1 30 1 90
2 30 1 30
2 30 0 30
2 30 0 30
2 30 0 30
2 30 1 60
2 30 0 30
2 30 1 60
2 30 0 30
2 30 0 30
2 30 0 30
2 30 1 60
2 30 1 90
2 30 0 30
Data:
DECLARE #t TABLE
(
ID INT ,
Duration INT ,
INDICATOR INT
)
INSERT INTO #t
VALUES ( 1, 30, 0 ),
( 1, 30, 1 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 1 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 0 ),
( 1, 30, 1 ),
( 1, 30, 1 ),
( 2, 30, 1 ),
( 2, 30, 0 ),
( 2, 30, 0 ),
( 2, 30, 0 ),
( 2, 30, 1 ),
( 2, 30, 0 ),
( 2, 30, 1 ),
( 2, 30, 0 ),
( 2, 30, 0 ),
( 2, 30, 0 ),
( 2, 30, 1 ),
( 2, 30, 1 ),
( 2, 30, 0 );
Solution1(bruteforce):
with indexed
as ( select * ,
row_number() over ( order by ( select null ) ) rn
from #t
),
recursion
as ( select * ,
duration as cumulative
from indexed
where rn = 1
union all
select t.* ,
case when t.indicator = 1 and t.id = r.id
then r.cumulative + t.duration
else t.duration
end
from indexed t
join recursion r on r.rn + 1 = t.rn
)
select * from recursion
option ( maxrecursion 0 )
Solution2:
with indexed as (select *, row_number() over (order by (select null)) rn from #t)
select *,
(select sum(duration) from indexed i2
where i1.id = i2.id and i2.rn <= i1.rn and
i2.rn >= isnull((select top 1 i3.rn from indexed i3
where i3.indicator = 0 and i3.rn <= i1.rn
order by i3.rn desc), 0))
from indexed i1

ordering the results of a query as specified in the where clause

I have a table QuotesTable - primary key is quotesid.
I have this sql-statement:
select * from QuotesTable where quotesid in (103,7,16,50,41,80,67,64)
This returns me the result in the following order:
7
16
41
.........
103 and so on.
but I need the results in the following order as specified in the query (103,7,16,50,41,80,67,64) as:
103,
7
16
.......
64 and so on.
Is there a way to achieve this one?
Try this:
select * from QuotesTable where quotesid in (103,7,16,50,41,80,67,64)
order by case quotesid when 103 then 1
when 7 then 2
when 16 then 3
when 50 then 4
when 41 then 5
when 80 then 6
when 67 then 7
when 64 then 8
end
If those values grow then you can create a table in database:
create table QuotesOrderingTable(quotesid int, orderid int)
go
fill it with appropriate values:
insert into QuotesOrderingTable values
(103, 1),
(7, 2),
(16, 3),
(50, 4),
(41, 5),
(80, 6),
(67, 7),
(64, 8),
(..., 9),
(..., 10),
...
and then use it to order by:
select qt.* from QuotesTable qt
join QuotesOrderingTable qot on qt.quotesid = qot.quotesid
where qt.quotesid in (103,7,16,50,41,80,67,64)
order by qot.orderid
Another option is doing it like this:
SELECT 1, * FROM QuotesTable WHERE quotesid = 103 UNION ALL
SELECT 2, * FROM QuotesTable WHERE quotesid = 7 UNION ALL
SELECT 3, * FROM QuotesTable WHERE quotesid = 16 UNION ALL
SELECT 4, * FROM QuotesTable WHERE quotesid = 50 UNION ALL
SELECT 5, * FROM QuotesTable WHERE quotesid = 41 UNION ALL
SELECT 6, * FROM QuotesTable WHERE quotesid = 80 UNION ALL
SELECT 7, * FROM QuotesTable WHERE quotesid = 67 UNION ALL
SELECT 8, * FROM QuotesTable WHERE quotesid = 64
ORDER BY 1
Not the prettiest, but atleast you aren't repeating the WHERE clause
Another variation, which looks a bit nicer:
SELECT * FROM QuotesTable q
JOIN (SELECT 1 ordering, 103 quotesid UNION ALL
SELECT 2 , 7 UNION ALL
SELECT 3 , 16 UNION ALL
SELECT 4 , 50 UNION ALL
SELECT 5 , 41 UNION ALL
SELECT 6 , 80 UNION ALL
SELECT 7 , 67 UNION ALL
SELECT 8 , 64) o ON o.quotesid = q.quotesid
ORDER BY o.ordering

mysql count zeros in sequence

I got mysql database and I need to get number of zeros in sequence and print them all with date from first zero, so for example I got a table like this
id, date, impuls_count
1, '12-05-15 12:00:00', 60
2, '12-05-15 12:01:00', 0
3, '12-05-15 12:02:00', 0
4, '12-05-15 12:03:00', 49
5, '12-05-15 12:04:00', 0
6, '12-05-15 12:05:00', 0
7, '12-05-15 12:06:00', 0
8, '12-05-15 12:07:00', 0
9, '12-05-15 12:08:00', 30
10, '12-05-15 12:09:00', 0
this should give the result like this:
'12-05-15 12:01:00', 2
'12-05-15 12:04:00', 4
'12-05-15 12:09:00', 1
I tried to solve it on my own but my query works very slow(I got 5000 rows in a table) and it sometimes prints same row twice
SELECT qwe.date, ile
FROM (SELECT p.date,
(SELECT COUNT(*)
FROM performance_v2
WHERE date > p.date
AND date <
(SELECT MIN(date)
FROM performance_v2
WHERE date > p.date AND impuls_count > 0)) ile
FROM performance_v2 p
WHERE p.impuls_count > 0
AND (date(p.date)
BETWEEN '2015-05-08%'
AND '2015-05-08%')
AND (time(p.date)
between '14:00:00' and '22:00:00')
ORDER BY 1) qwe
WHERE ile > 0
In MySQL, this is easiest to solve using variables. The idea is to have a counter increment each time the value of impuls_count changes. This defines groups of common values. You can then filter the values and aggregate to get what you want:
select min(date), count(*)
from (select t.*,
(#g := if(#ic = impuls_count, #g,
if(#ic := impuls_count, #g + 1, #g + 1)
)
) as grp
from table t cross join
(select #ic := 0, #g := 0)
order by id
) t
where impuls_count = 0
group by grp

Calculation of Percentage for Rows within particular range

I have a List of students name and Marks Stored in a table like below.
CREATE TABLE StudentsList(StudentId INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50),
Marks INT);
INSERT INTO StudentsList(Name, Marks)
VALUES('Student A', 20),
('Student B', 45),
('Student C', 90),
('Student D', 81),
('Student E', 50),
('Student F', 10),
('Student G', 85),
('Student H', 41),
('Student I', 66),
('Student J', 65),
('Student K', 05),
('Student L', 20),
('Student M', 19),
('Student N', 80),
('Student O', 90),
('Student P', 91),
('Student Q', 10),
('Student R', 29);
I want to Group the no of students based on the mark range and percentage they contribute in whole.
MarkRange NoOfStudents Percentage
0 - 20 4 22.22
20 - 50 5 27.77
50 - 70 3 16.66
70 - 90 3 16.66
90 3 16.66
I Tried the Below Query and Brought the Result for Students between 0 -20
SELECT COUNT(*) , COUNT(*)/(T.total)* 100
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks < 20
How can I do this by using single Query
Try this:
SELECT CONCAT(A.minRange, ' - ', A.maxRange) MarkRange, COUNT(sl.Name) NoOfStudents,
(SELECT COUNT(sl.Name) / COUNT(*) * 100 FROM StudentsList) Percentage
FROM studentslist sl
INNER JOIN (SELECT 1 id, 0 minRange, 20 maxRange
UNION
SELECT 2 id, 20 minRange, 50 maxRange
UNION
SELECT 3 id, 50 minRange, 70 maxRange
UNION
SELECT 4 id, 70 minRange, 90 maxRange
UNION
SELECT 5 id, 90 minRange, 100 maxRange
) AS A ON sl.Marks >= A.minRange AND sl.Marks < A.maxRange
GROUP BY A.id;
SELECT COUNT(*) , COUNT(*)/(T.total)* 100, '0 - 20' AS Range
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks < 20
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100, '20 - 50' AS Range
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 20 and Marks < 50
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100, '50 - 70' AS Range
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 50 and Marks < 70
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100, '70 - 90' AS Range
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 70 and Marks < 90
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100, '90 Above' AS Range
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 90
I found an Answer Using Union.
If there is some other solution kindly suggest me
Try this:
SELECT COUNT(*) as 'number of students', COUNT(*)/(T.total)* 100 as 'Percentage', '0 - 20' AS 'Range'
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks < 20
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100 as 'Percentage', '20 - 50' AS 'Range'
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks >= 20 and Marks < 50
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100 as 'Percentage', '50 - 70' AS 'Range'
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks >= 70 and Marks < 90
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100 as 'Percentage', '70 - 90' AS 'Range'
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks >= 70 and Marks < 90
UNION
SELECT COUNT(*) , COUNT(*)/(T.total)* 100 as 'Percentage', 'Above 90' AS 'Range'
FROM StudentsList,
(SELECT COUNT(*) AS total
FROM StudentsList) AS T
WHERE Marks >= 0 and Marks >= 90
order by 3
output:
NUMBER OF STUDENTS PERCENTAGE RANGE
4 22.2222 0 - 20
5 27.7778 20 - 50
3 16.6667 50 - 70
3 16.6667 70 - 90
3 16.6667 Above 90
You can see how it runs here