Select right percentile from joined table - mysql

How to select right value from joined table based on the value in more than one column.
I started sqlfiddle: http://sqlfiddle.com/#!9/f92daa/2
Main table:
id val
1 1
2 4
3 67
4 78
5 22
Joined table:
obs_id perc1 perc2 perc3 perc4 perc5 perc6 perc7 perc8 perc9 perc10
1 1 2 3 4 5 6 7 8 9 10
2 1 2 3 4 5 6 7 8 9 10
3 10 20 30 40 50 60 70 80 90 100
4 10 20 30 40 50 60 70 80 90 100
5 10 20 30 40 50 60 70 80 90 100
Expected result should be:
id val perc
1 1 1
2 4 4
3 67 70
4 78 80
5 22 30
perc - should be number from perc table (joined by ids) not greater than highest possible value form columns perc1-10
Table perc stores percentiles of observations. The goal is to find which interval hit the value from main table and take the upper band.

You can use a case expression to do this. This assumes perc1 < perc2 < perc3 < perc4 < ... and so on.
SELECT obs.id, obs.val,
case when val <= perc1 then perc1
when val <= perc2 then perc2
when val <= perc3 then perc3
when val <= perc4 then perc4
when val <= perc5 then perc5
when val <= perc6 then perc6
when val <= perc7 then perc7
when val <= perc8 then perc8
when val <= perc9 then perc9
when val <= perc10 then perc10
end
perc
FROM obs
INNER JOIN perc
ON obs.id = perc.obs_id
SQL Fiddle

If you are sure that perc table will have always the values in ascending order then
select
a.id, a.val,
case
when b.perc1 >= a.val then b.perc1
when b.perc2 >= a.val then b.perc2
when b.perc3 >= a.val then b.perc3
when b.perc4 >= a.val then b.perc4
when b.perc5 >= a.val then b.perc5
when b.perc6 >= a.val then b.perc6
when b.perc7 >= a.val then b.perc7
when b.perc8 >= a.val then b.perc8
when b.perc9 >= a.val then b.perc9
when b.perc10 >= a.val then b.perc10
end as perc
from idval a , perc b
where a.id = b.obs_id

Related

MYSQL GROUP BY and SUM with clause

Hello there are two tables
Interval
id
is_full
1
1
2
0
3
0
entry_penalty
interval_id
entry_id
amount
2
14
55
3
14
7
3
14
1
1
15
4
1
15
8
2
15
11
So i am trying to display Sum of all entry_penalties per interval, twist is even if there is no relation between entry_penalty and interval table i should display full course interval sum per entry_id (related to is_full field).
For example total results should be in this case
interval_id
entry_id
amount
1
14
63
2
14
55
3
14
8
1
15
23
2
15
11
I have tried with sub query but it ignores to do calculation when there is no relation between entry_penalties and interval tables regarding is_full column.
My code so far.
SELECT
ep.interval_id,
IF (
i.is_full,
(
SELECT SUM(ep2.amount) * 1000 FROM entry_penalty as ep2
WHERE ep2.entry_id = ep.entry_id
),
SUM(ep.amount) * 1000
) as penalty_time,
ep.entry_id
FROM entry_penalty ep
INNER JOIN \`interval\` i ON i.id = ep.interval_id
WHERE ep.entry_id IN (:entryIds)
GROUP BY interval_id, entry_id`
I would propose to deal with the two cases (full, not full) separately, and then use union all to combine the two results:
SELECT i.id, ep.entry_id, SUM(ep.amount)
FROM `interval` i,
entry_penalty ep
WHERE i.is_full
GROUP BY i.id, ep.entry_id
UNION ALL
SELECT i.id, ep.entry_id, SUM(ep.amount)
FROM entry_penalty ep
INNER JOIN `interval` i
ON ep.interval_id = i.id
AND NOT i.is_full
GROUP BY i.id, ep.entry_id
ORDER BY 2, 1
See it run on dbfiddle.uk, where it outputs:
id
entry_id
SUM(ep.amount)
1
14
63
2
14
55
3
14
8
1
15
23
2
15
11

how to detect peak of two columns in sql

I have two columns in mysql:
row A B
1 90 80
2 80 57
3 57 5
4 48 30
5 30 15
I need to compare the value of B and the next value of A, how could I detect a peak when B is 5 (row 3) and A is 48 (row 4)? New column can be added to say whether a peak is detected.
The result should be:
row A B peak_detection
1 90 80 0
2 80 57 0
3 57 5 0
4 48 30 1
5 30 15 0
Thank you
In steps:
How big is the difference:
SELECT
A,
B,
LAG(A) over (order by r)-B difference
FROM Table1
Select the max row:
SELECT r
FROM (
SELECT
r,
A,
B,
LAG(A) over (order by r)-B difference
FROM Table1
) t2
ORDER BY difference DESC
LIMIT 1
Add the column peak_detection:
SELECT
A,
B,
IF(Table1.r=t2.r,1,0) peak_detection
FROM Table1
LEFT JOIN (
SELECT r
FROM (
SELECT
r,
A,
B,
LAG(A) over (order by r)-B difference
FROM Table1
) t2
ORDER BY difference DESC
LIMIT 1
) t2 on t2.r=Table1.r
See: dbfiddle
output:
A
B
peak_detection
90
80
0
80
57
0
57
5
1
48
30
0
30
15
0
P.S. code improvement can be done (and might be needed) on the last query, if needed.

Query within a query to obtain certain value with multiple joins in MySQL Select

I have 3 tables that I have joined in the MySQL query below. All works well EXCEPT I would like the timeadj value with a 1 in column countingtime
to show from table 'data', not the first timeadj value the query finds.
I know this needs to be a query within a query but I am going around in circles and getting no where.
SELECT ttt_entries.tttid, ttt_teams.teamname, data.RacersInTeam,
ttt_entries.CoffeeClass, SEC_TO_TIME(data.timeadj),
COUNT(IF(data.division=5,1,NULL)) 'A+',
COUNT(IF(data.division=10,1,NULL)) A,
COUNT(IF(data.division=20,1,NULL)) B,
COUNT(IF(data.division=30,1,NULL)) C,
COUNT(IF(data.division=40,1,NULL)) D
FROM ttt_entries
INNER JOIN ttt_teams
ON ttt_entries.tttid = ttt_teams.tttid
INNER JOIN (SELECT * FROM data ORDER BY data.countingtime DESC) as data
ON ttt_entries.tttid = data.teamid
WHERE ttt_entries.eventDate = DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE()) >4, -5, 2) + DAYOFWEEK(CURDATE())) * -1 DAY) -- last Thursday
AND data.wtrlid = '22'
GROUP BY ttt_teams.tttid
ORDER BY data.timeadj ASC
For example.... In Team 1 (data.teamid=1) there are 8 time values in timeadj. In the adjacent column (countingtime) is a value either 1 or 0 but only ever 1x 1 per team.
Table ttt_entries
ID tttid CoffeeClass
1 23 Mocha
2 52 Espresso
3 6 Frappe
Table ttt_teams
tttid Name
6 Team A
23 Team 1
52 Team 2
Table 'data'
id wtrlid teamid timeadj countingtime division
1 22 23 3467.123 0 10
2 22 23 3467.125 0 20
3 22 23 3467.432 0 10
4 22 23 3469.000 1 10
5 22 23 3469.112 0 10
6 22 23 3468.987 0 5
My code brings back
tttid teamname RacersInTeam CoffeeClass Time A+ A B C D
23 Team 1 6 Mocha 3467.123 1 4 1 0 0
I need it to bring back the same data but a different time:
tttid teamname RacersInTeam CoffeeClass Time A+ A B C D
23 Team 1 6 Mocha 3469.000 1 4 1 0 0
You can try below way -
SELECT ttt_entries.tttid, ttt_teams.teamname, data.RacersInTeam,
ttt_entries.CoffeeClass, SEC_TO_TIME(max(case when countingtime=1 then data.timeadj end)),
COUNT(IF(data.division=5,1,NULL)) 'A+',
COUNT(IF(data.division=10,1,NULL)) A,
COUNT(IF(data.division=20,1,NULL)) B,
COUNT(IF(data.division=30,1,NULL)) C,
COUNT(IF(data.division=40,1,NULL)) D
FROM ttt_entries
INNER JOIN ttt_teams
ON ttt_entries.tttid = ttt_teams.tttid
INNER JOIN (SELECT * FROM data ORDER BY data.countingtime DESC) as data
ON ttt_entries.tttid = data.teamid
WHERE ttt_entries.eventDate = DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE()) >4, -5, 2) + DAYOFWEEK(CURDATE())) * -1 DAY) -- last Thursday
AND data.wtrlid = '22'
GROUP BY ttt_teams.tttid
ORDER BY data.timeadj ASC

mysql query issue while data recieving

Sql Query to retrieve data using table ##
I need query to get the count of participants in each level .
Note: participant who is in level 4 should not be in other levels ex: Level 3,2,1
Table :
ID Level date
38 1 06 -05
38 2 08 -05
38 3 12 -05
38 4 13 -05
39 1 13 -05
39 2 13 -05
40 1 12 -05
Needed Output:
Count Level
1 1
1 2
0 3
1 4
You can define an artadditional table (or create a subquery) which would return the 30 rows
e.g
(select now() as d
union
select date_sub(now(), interval 1 day) as d
union
select date_sub(now(), interval 2 day) as d
union
...
select date_sub(now(), interval 29 day) as d) all_dates_source
Then you can LEFT JOIN your query result to the all_dates_source
SELECT i,
Logindate2,
ifnull(Logindate2,0, Logins2) as Logins2
FROM (...) all_dates_source
LEFT JOIN (yourQueryHere) sourceQuery
on all_dates_source.d=sourceQuery.Logindate2
SELECT count(t2.ppt) as count,t1.Level as level
FROM courselevel t1
LEFT JOIN
(SELECT ppt,MAX(Level) as Level
FROM courselevel
GROUP BY ppt ) t2 on t1.ppt=t2.ppt and t1.Level=t2.Level
GROUP BY t1.Level

change multi rows to one row-multi column

For some reason i have to do this. i have a query that have result like this :
limit usage tariff total
0 10 10 700 7000
11 20 10 900 9000
21 30 10 1800 18000
31 > 11 2700 29700
the query return 4 rows maximum (like above) or sometime just 3 rows.
I want to change the rows to just one row and multi column like this (the list below just one row):
limit1 usage1 tariff1 total1 limit2 usage2 tariff2 total2
0 10 10 700 7000 11 20 10 900 9000
limit3 usage3 tariff3 total3 limit4 usage4 tariff4 total4
21 30 10 1800 18000 31 > 11 2700 29700
if the query return just 3 rows, the values in column limit4 until total4 will be empty. I dont know how to do like that.
EDITED
I add one ID column so the list will be :
ID limit usage tariff total
1 0 10 10 700 7000
2 11 20 10 900 9000
3 21 30 10 1800 18000
4 31 > 11 2700 29700
I try to make it one row like this :
SELECT e.*,f.id AS id4,f.limit AS limit4,f.usage AS usage4,f.tariff AS tariff4,f.total AS total4
FROM
(SELECT c.*,d.id AS id3,d.limit AS limit3,d.usage AS usage3,d.tariff AS tariff3,d.total AS total3
FROM
(SELECT b.id AS id1,b.limit AS limit1,b.usage AS usage1,b.tariff AS tariff1,b.total AS total1,
a.id AS id2,a.limit AS limit2,a.usage AS usage2,a.tariff AS tariff2,a.total AS total2
FROM testtariff a
INNER JOIN testtariff b ON a.id!=b.id
LIMIT 1) c INNER JOIN testtariff d ON c.id1 != d.id AND c.id2 != d.id
LIMIT 1) e INNER JOIN testtariff f ON e.id1 != f.id AND e.id2 != f.id
AND e.id3 != f.id
LIMIT 1
it work as i expected for 4 rows but not work for 3 rows. should i use cursor ?
This is generally called a "pivot". Here's how you do it:
select
'0 10' as limit1,
sum(limit between 0 and 10 * usage) as usage1,
sum(limit between 0 and 10 * tariff) as tariff1,
sum(limit between 0 and 10 * usage * tariff) as total1,
'11 20' as limit2,
sum(limit between 11 and 20 * usage) as usage2,
sum(limit between 11 and 20 * tariff) as tariff2,
sum(limit between 11 and 20 * usage * tariff) as total2,
-- etc
from mytable
group by 1,5 -- etc
This works because limit between x and x is 1 if true and 0 if false, so using multiplying by this is a simple way to filter the results into different groups.