Rank columns MySQL - mysql

I would like to rank my columns. (Column 1, column2) and add an overallRank. But I have no idea how to do this. See table 1 and 2 for my example. Table 1 is the current situation and I would like to go to table 2. So I calculate the ranks and store them in my new table.
I’d like to rank Column1 high -> low, Column2 low -> high. For the overallRANK I would like to combine the column ranks with different weights. 1 * Colomn1RANK + 2 * Colomn1RANK low -> high.
Is this even possible, if so, how?

You can add the rank using variables. In this case, you need to do it twice:
select t.*,
(col1rank + 2 * col2rank) as overallrank
from (select t.*, (#rn2 := #rn2 + 1) as col2rank
from (select t.*, (#rn1 := #rn1 + 1) as col1rank
from table t cross join
(select #rn1 := 0) vars
order by col1 desc
) t cross join
(select #rn2 := 0) vars
order by col2 asc
) t;
It is unclear whether overallrank is just a combination of the two ranks, or whether you want another rank on top of it. If the latter, then it is just another order by and ranking variable in the outer query.

Related

Trying to put a variable into limit to find a median

I trying to use mysql to solve the following solutions:
https://www.hackerrank.com/challenges/weather-observation-station-20/problem
Understanding that a variable cannot be put into LIMIT statement (from this )
My approach>
to declare a new variable to record rowIDs, and use rowID to retrieve the record in the middle.
However, it seems that rowID is not working well.
Could anyone give me some advises?
SELECT ROUND(COUNT(LAT_N)/2,0) FROM STATION into #count;
SELECT ROUND(a.LAT_N,4) FROM (
SELECT *,#row := #row + 1 FROM STATION s, (SELECT #row := 0) r
WHERE #row <=#count
ORDER BY s.LAT_N ASC) a
ORDER BY a.LAT_N DESC LIMIT 1;`
If you are running MySQL 8.0, this is simpler done with window functions:
select round(avg(lat_n), 4) median_lat_n
from (
select s.*, row_number() over(orer by lat_n) rn
from station s
where lat_n is not null
) s
where rn * 2 in (rn - 1, rn, rn + 1)
In earlier versions, variables make it bit tricky; we need one more level of nesting to make it safe:
select round(avg(lat_n), 2) median_lat_n
from (
select s.*, #rn := #rn + 1 rn
from (select * from station order by lat_n) s
cross join (select #rn := 0) p
) s
where rn * 2 in (rn - 1, rn, rn + 1)
The logic is as follows: first enumerate the rows, ordered by lat_n. If the row count is uneven, we pick the middle row; if it is even, we take the average of the two middle values.

Inserting Data at the certain positon of a column in a table mysql

Is it possible to insert data at the start on a column? Lets say like this.
What i mean is,is it possible to insert a bunch of data from 1 column of a table into another table starting at the row position?
tysm
SQL tables represent unordered sets. So, you cannot do exactly what you want. You can put the data side-by-side using variables and aggregation:
select max(col1) as col1, max(col2) as col2
from ((select (#rn1 := #rn1 + 1) as rn, col1, NULL as col2
from table1 t1 cross join (select #rn1 := 0) params
order by ??
) union all
(select (#rn2 := #rn2 + 1) as rn, NULL, col1
from table2 t2 cross join (select #rn2 := 0) params
order by ??
)
) tt
group by rn;
The order by ?? is for the column that specifies the ordering. If you don't care about the ordering, then just remove the order by. The ordering within the two columns will be arbitrary.

MySQL: Limiting result for WHERE IN list

Let's say there are millions of records in my_table.
Here is my query to extract rows with a specific name from list:
SELECT * FROM my_table WHERE Name IN ('name1','name2','name3','name4')
How do I limit the returned result per name1, name2, etc?
The following query would limit the whole result (to 100).
SELECT * FROM my_table WHERE Name IN ('name1','name2','name3','name4') LIMIT 100
I need to limit to 100 for each name.
This is a bit of a pain in MySQL, but the best method is probably variables:
select t.*
from (select t.*,
(#rn := if(#n = name, #rn + 1,
if(#n := name, 1, 1)
)
) as rn
from my_table t cross join
(select #n := '', #rn := 0) params
order by name
) t
where rn <= 100;
If you want to limit this to a subset of the names, then add the where clause to the subquery.
Note: If you want to pick certain rows -- such as the oldest or newest or biggest or tallest -- just add a second key to the order by in the subquery.
Try
SELECT * FROM my_table WHERE Name IN ('name1','name2','name3','name4') FETCH FIRST 100 ROWS ONLY

Select every 'nth row in descending order

SELECT * FROM ( SELECT #row := #row +1 AS rownum, [column name] FROM ( SELECT * FROM [table name] ) WHERE rownum % 5 = 1
This does indeed return every 5th row, but in ascending order. What I want is that it first gets all the data, put them in descending order and THEN apply the filter.
If you filter it first and then put it in descending order, it will not start with the latest data added (4/5th of the time).
I would like to know how one should do this.
Thanks in advance
Edit: For people with the same problem, this is what I used:
SELECT * FROM
(SELECT rank, id, Temperature FROM
(SELECT *, #rownum := #rownum + 1 AS rank FROM temperature_room1,
(SELECT #rownum := 0) r) AS T ORDER BY id DESC) AS J WHERE rank % 5 = 1
Select everything from:
Select rank, id and Temperature from:
Select everything and rownumber as rank from the table as t ordered by ID in descending order
Finally, only output the row numbers which can be divided by 5 and the remainder is 1
Don't quote me on this, I'm a big noob regarding SQL stuff. It works for me, so I'm happy.
seems like you just need an order by dec on the desired column in one of the three queries. I think the second one as order by applies to the select at the same level. ans since you want your rownum ordered desc... seems like that's the place...
SELECT *
FROM ( SELECT #row := #row +1 AS rownum, [column name]
FROM ( SELECT * FROM [table name] )
ORDER BY [column name] desc
)
WHERE rownum % 5 = 1

Mysql recursive substracting and multiplying values

Couldn't really explain my problem with words, but with an example I can show it clearly:
I have a table like this:
id num val
0 3 10
1 5 12
2 7 12
3 11 15
And I want to go through all the rows, and calculate the increase of the "num", and multiply that difference with the "val" value. And when I calculated all of these, I want to add these results together.
This is the mathematical equation, that I want to run on the table:
Result = (3-0)*10 + (5-3)*12 + (7-5)*12 + (11-7)*15
138 = Result
Thank you.
You can do with mysql variables, but you will still get one record for each entry.
select
#lastTotal := #lastTotal + ( (yt.num - #lastNum) * yt.val ) thisLineTotal,
#lastNum := yt.num as saveForNextRow,
yt.id
from
yourTable yt,
( select #lastTotal := 0,
#lastNum := 0 ) sqlvars
order by
id
This SHOULD give you what you want to confirm the calculations to each record basis.
Now, to get the one record and one column result, you can wrap it such as
select
pq.thisLineTotal
from
(above entire query ) as pq
order by
pq.id DESC
limit 1
Assuming the IDs are consecutive as your sample data suggests, just join the table to itself:
select sum((t1.num-ifnull(t2.num,0))*t1.val) YourValue
from YourTable t1
left join YourTable t2
on t2.id = t1.id - 1;
http://www.sqlfiddle.com/#!2/40b9f/12
This will give you the total. Make sure to order in the order you wish - I have ordered by id
SET #runtot:=0;
SET #prevval:=0;
select max(rt) as total FROM (
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID) tot
If you want to see the details of the calculation, leave out the outer select as so:
SET #runtot:=0;
SET #prevval:=0;
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID
If it is possible to have negative numbers for your column values, using max(rt) won't work for the total. You should then use:
SET #runtot:=0;
SET #prevval:=0;
select #runtot as total FROM (
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID) tot LIMIT 1