I have a very simple table like that:
CREATE TABLE IF NOT EXISTS LuxLog (
Sensor TINYINT,
Lux INT,
PRIMARY KEY(Sensor)
)
It contains thousands of logs from different sensors.
I would like to have Q1 and Q3 for all sensors.
I can do one query for every data, but it would be better for me to have one query for all sensors (getting Q1 and Q3 back from one query)
I though it would be a fairly simple operation, as quartiles are broadly used and one of the main statistical variables in frequency calculation. The truth is that I found loads of overcomplicated solutions, while I was hoping to find something neat and simple.
Anyone can give me a hint?
Edit: This is a piece of code that I found online, but it is not working for me:
SELECT SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT( -- 1) make a sorted list of values
Lux
ORDER BY Lux
SEPARATOR ','
)
, ',' -- 2) cut at the comma
, 75/100 * COUNT(*) -- at the position beyond the 90% portion
)
, ',' -- 3) cut at the comma
, -1 -- right after the desired list entry
) AS `75th Percentile`
FROM LuxLog
WHERE Sensor=12
AND Lux<>0
I am getting 1 as return value, while it should be a number that can be divided by 10 (10,20,30.....1000)
Well to use NTILE is very simple but it is a Postgres Function. You basically just do something like this:
Here is a simple example I made for you on SQLFiddle: http://sqlfiddle.com/#!15/7f05a/1
In MySQL you would use RANK... Here is the SQLFiddle for that: http://www.sqlfiddle.com/#!2/d5587/1 (this comes from the Question linked below)
This use of MySQL RANK() comes from the Stackoverflow answered here: Rank function in MySQL
Look for the answer by Salman A.
See SqlFiddle : http://sqlfiddle.com/#!9/accca6/2/6 Note : for the sqlfiddle I've generated 100 rows, each integer between 1 and 100 has a row, but it is a random order (done in excel).
Here is the code :
EDIT :
Underlying reasoning is as follows : For quartile 1 we want to get 25% from the top so we want to know how much rows there are, that's :
Now that we know the number of rows, we want to know what is 25% of that, it is this line :
Then to find a quartile we want to order the LuxLog table by Lux, then to get the row number "@quartile", in order to do that we set the OFFSET to @quartile to say that we want to start our select from the row number @quartile and we say limit 1 to say that we want to retrieve only one row. That's :
We do (almost) the same for the other quartile, but rather than starting from the top (from higher values to lower) we start from the bottom (it explains the ASC).
But for now we just have strings stored in the variables @sql_q1 and @sql_q3, so the concatenate them, we union the results of the queries, we prepare the query and execute it.
Here's a query I came up with for calculating quartiles; it runs in ~0.04s w/ ~5000 table rows. I included the min/max values as I am ultimately using this data to build the four quartile ranges:
Fiddle here: http://sqlfiddle.com/#!9/58c0e2/1
There are certainly performance issues; I'd love if anyone has feedback on how to improve this.
Sample data list:
Sample query output:
I use this solution with a MYSQL function :
x is the centile you want
array_values your group_concat values order and separated by ,
Example :
Or you could use rank like this:
And this will give just one record for each quartile:
EDIT: The sqlFiddle example (http://sqlfiddle.com/#!9/a14a4/17) looks like this after this is removed
Something like this should do it:
Here's the complete example: