Calculate values in ranges for every row in table - mysql

I have table where some objects are described.
id title low middle high
-- ------- --- ------ ----
1 Object1 2 5 11
2 Object2 2 6 11
3 Object3 2 5 15
4 Object4 2 5 11
5 Object5 2 5 11
6 Object6 2 5 11
7 Object7 2 5 15
So for Object1 values 0…2 should be calculated as lowCount , 3…5 as middleCount, 6…11 as highCount, all values greater than 11 as overCount.
Every object could get 3 values and it’s necessary to count how many values are in low, medium, high and over ranges
So For example with values 1, 4, 12 I expect to get the result
id title low middle high lowCount middleCount highCount overCount
-- ------- --- ------ ---- -------- ----------- --------- ---------
1 Object1 2 5 11 1 0 1 1
2 Object2 2 6 11 1 1 0 1
3 Object3 2 5 15 1 0 2 0
4 Object4 2 5 11 1 0 1 1
5 Object5 2 5 11 1 0 1 1
6 Object6 2 5 11 1 0 1 1
7 Object7 2 5 15 1 0 2 0
For this issue I use the query:
SELECT
`st`.`id`, `st`.`title`, `st`.`low`, `st`.`middle`, `st`.`high`
, (IF((1 >= 0 AND 1 <= `st`.`low`), 1, 0) + IF((4 >= 0 AND 4 <= `st`.`low`), 1, 0) + IF((12 >= 0 AND 12 <= `st`.`low`), 1, 0)) as `lowCount`
, (IF((1 > `st`.`low` AND 1 <= `st`.`middle`), 1, 0) + IF((4 > `st`.`low` AND 4 <= `st`.`middle`), 1, 0) + IF((12 > `st`.`low` AND 12 <= `st`.`middle`), 1, 0)) as `middleCount`
, (IF((1 > `st`.`middle` AND 1 <= `st`.`high`), 1, 0) + IF((4 > `st`.`middle` AND 4 <= `st`.`high`), 1, 0) + IF((12 > `st`.`middle` AND 12 <= `st`.`high`), 1, 0)) as `highCount`
, (IF((1 > `st`.`high`), 1, 0) + IF((4 > `st`.`high`), 1, 0) + IF((12 > `st`.`high`), 1, 0)) + 2 as `overCount`
FROM
`some_table` `st`
I don't like this construction
, (IF((1 >= 0 AND 1 <= `st`.`low`), 1, 0) + IF((4 >= 0 AND 4 <= `st`.`low`), 1, 0) + IF((12 >= 0 AND 12 <= `st`.`low`), 1, 0)) as `lowCount`
What could I use instead

if is acceptable. The case statement is longer, but standard SQL. However, in MySQL, you can also do:
select `st`.`id`, `st`.`title`, `st`.`low`, `st`.`middle`, `st`.`high`,
((1 >= 0 AND 1 <= `st`.`low`) + (4 >= 0 AND 4 <= `st`.`low`) + (12 >= 0 AND 12 <= `st`.`low`)) as `lowCount`,
((1 > `st`.`low` AND 1 <= `st`.`middle`) + (4 > `st`.`low` AND 4 <= `st`.`middle`) + (12 > `st`.`low` AND 12 <= `st`.`middle`)) as `middleCount`,
((1 > `st`.`middle` AND 1 <= `st`.`high`) + (4 > `st`.`middle` AND 4 <= `st`.`high`) + (12 > `st`.`middle` AND 12 <= `st`.`high`)) as `highCount`
FROM `some_table` `st`;
In other words, MySQL treats "true" as 1 and "false" as 0, so you can just add up the boolean values to get the counts.

Related

case when mysql with multiple conditions

I made the following case when statement in my sql:
SELECT
*,
CASE
WHEN lead_time < 14 THEN 0
WHEN (14 <= lead_time < 21 AND days_passed = 8) THEN 1
WHEN
(21 <= lead_time < 28
AND days_passed = 15)
THEN
1
WHEN
(28 <= lead_time < 42
AND days_passed = 22)
THEN
1
WHEN
(42 <= lead_time < 56
AND days_passed = 36)
THEN
1
WHEN
(56 <= lead_time < 84
AND days_passed = 29)
THEN
1
WHEN
(56 <= lead_time < 84
AND days_passed = 50)
THEN
1
WHEN (lead_time > 84 AND days_passed = 36) THEN 1
WHEN (lead_time > 84 AND days_passed = 57) THEN 1
ELSE 0
END AS send_email
I do not get any error. However, when I check the results I get:
# lead_time, days_passed, send_email
99, 15, 1
99, 22, 1
99, 15, 1
99, 8, 1
99, 8, 1
99, 8, 1
99, 8, 1
85, 29, 1
57, 50, 1
18, 36, 1
99, 22, 1
99, 22, 1
99, 22, 1
99, 22, 1
99, 22, 1
15, 15, 1
15, 15, 1
99, 8, 1
99, 8, 1
99, 8, 1
99, 8, 1
It seems as if the 'and' in the query behaves as an 'or'. Any idea why?.
Thanks,
MySQL doesn't support a syntax:
min <= expr <= max
Such kinds of expressions can be written as:
WHERE expr >= min AND expr <= max
or with using operator BETWEEN (please note that BETWEEN is inclusive):
WHERE expr BETWEEN min AND max
Comparison operators in MySQL: http://dev.mysql.com/doc/refman/5.5/en/comparison-operators.html
The comparison 14 <= lead_time < 21 AND days_passed = 8 is checked in sequence, so in a way you have:
((14 <= lead_time) < 21) AND (days_passed = 8)
Which is always true because 14 <= lead_time equals 1 and thus your comparison is equal to:
( 1 < 21 ) AND days_passed = 8
You should use a between or an and for each comparison.

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

MySQL calculate from same value

i have this query:
SELECT `item_code`,
`q_rr`,
`q_srs`,
#running_bal := #running_bal + (`q_rr` - `q_srs`) as `Balance`
FROM records, (SELECT #running_bal := 0) tempName
order by
records.item_code,
records.date
which results is:
item_code | q_rr | q_srs | balance
--------------------------------------------
0F02206A 2 0 2
BR00113D 3 0 5
BR00114D 10 0 15
BR00114D 0 1 14
BR00114D 0 1 13
BR00115D 20 0 33
BR00115D 0 1 32
BR00115D 0 1 31
need help to make the result to calculate the balance, if q_rr(+) and q_srs(-) and calculate per item_code.
item_code | q_rr | q_srs | balance
--------------------------------------------
0F02206A 2 0 2
BR00113D 3 0 3
BR00114D 10 0 10
BR00114D 0 1 9
BR00114D 0 1 8
BR00115D 20 0 20
BR00115D 0 1 19
BR00115D 0 1 18
I added #code variable to track item_code changes:
SELECT
r.item_code,
r.q_rr,
r.q_srs,
#running_bal := IF(#code = r.item_code, #running_bal, 0) + (r.q_rr - r.q_srs) as Balance,
#code := r.item_code AS dummy
FROM
records r
CROSS JOIN
(SELECT #running_bal := 0, #code := '') tempName
ORDER BY
r.item_code,
r.date
SQL Fiddle: http://sqlfiddle.com/#!2/04ef2b/11
You can eliminate dummy column by putting this as subquery in another select:
SELECT item_code, q_rr, q_srs, Balance
FROM (
-- there put first query
) r
SQL Fiddle: http://sqlfiddle.com/#!2/04ef2b/12
Try this:
SELECT `item_code`,
`id`,
`type`,
#running_bal := case when type = 0 then id
else #running_bal + (`id` - `type`) end as `Balance`
FROM supportContacts, (SELECT #running_bal := 0) tempName
order by
supportContacts.item_code
SQL FIDDLE

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

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

MYSQL select rows when criteria in at least x colums is met?

Another MYSQL question where I could not find an answer:
I have this table and I want to to get all ID's which have SN>7 AND reps > 4 and where in columns 1-6 a certain criteria is met at least x times.
For example where at least 3 cells in columns col1-col6 have a value > 1.
The first part is easy (SELECT * FROM table WHERE SN > 7 AND reps > 4....) but I can't figure out second part.
Thanks!
ID SN reps col1 col2 col3 col4 col5 col6
A 12 3 0.6 1 3 -2 1 3
B 6 5 3.2 1.1 -3.3 3 0 0
C 300 6 1.3 -0.4 0 0.6 -0.5 -3.3
Try:
SELECT * FROM table
WHERE SN > 7 AND reps > 4 and
(case when `1` > 1 then 1 else 0 end +
case when `2` > 1 then 1 else 0 end +
case when `3` > 1 then 1 else 0 end +
case when `4` > 1 then 1 else 0 end +
case when `5` > 1 then 1 else 0 end +
case when `6` > 1 then 1 else 0 end) >= 3
select * from table where sn> 7 and reps > 4 and
(((`1` > 1) + (`2` > 1 ) + (`3` > 1) + (`4` > 1) + (`5` >1 ) + (`6` > 1)) >= 3)