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.
Related
I've got this query running on two different fiddle sites, both set to use MySQL 5.6:
SELECT name, rank, position FROM(
SELECT name, position,
#rank:= IF(#prev = name, #rank + 1, 1) AS rank,
#prev:= name
FROM (SELECT * FROM drivers
LEFT JOIN results on drivers.id = results.driver_id
JOIN (SELECT #rank := 1) AS init
ORDER BY name, results.position ASC) AS temp
) AS derived WHERE rank <= 3 ORDER BY name, rank
It's supposed to give the top 3 finishing positions of each driver. The query works on fiddle #1, but not on fiddle #2 or the production server, although all three of them are running on MySQL 5.6.
Is there a setting that I'm missing?
Fiddle #1 - working
Fiddle #2 - not working
You can use the following solution:
SELECT name, rank, position FROM (
SELECT name, position,
#rank:= IF(#prev = name, #rank + 1, 1) AS rank,
#prev:= name
FROM (
SELECT *
FROM drivers LEFT JOIN results ON drivers.id = results.driver_id
JOIN (SELECT #rank := 1) AS init_rank
JOIN (SELECT #prev := '') AS init_prev
ORDER BY name, results.position ASC
) AS temp
) AS derived
WHERE rank <= 3
ORDER BY name, rank
I added the initialization for the #prev variable on a JOIN too.
different demos:
demo on db-fiddle.com
demo on sqlfiddle.com
demo on dbfiddle.uk
Since MySQL 8.0 you can use the built-in RANK window function. So you don't need the #prev or #rank variables:
SELECT name, `rank`, position FROM (
SELECT name, position, RANK() OVER (PARTITION BY name ORDER BY name, position) AS `rank`
FROM drivers LEFT JOIN results ON drivers.id = results.driver_id
) AS derived
WHERE `rank` <= 3
ORDER BY name, `rank`
demo on dbfiddle.uk
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
I am having a query and I want to perform the operation like
select *
from (query which i wrote) as x
where
(select count(*)
from x as y
where x.location=y.location
and x.count>=y.count)<=3;
It was giving error
instead of x, I can add the query which I wrote. but the query is pretty much big. when I tried the above query it is giving table doesn't exist error. is there a way to perform the above operation? kindly help me.
You cannot re-use a table alias like that. Instead, you need to copy the subquery. Or use variables:
select q.*
from (select q.*,
(#rn := if(#l = location, #rn + 1,
if(#l := location, 1, 1)
)
) as rn
from (query which i wrote) q cross join
(select #l := '' , #rn := 0) params
order by location, count desc
) q
where rn <= 3;
I am working on migration from MS SQL Server to MariaDB 10.0. I have query which use RANK() PARTITION BY. I already created some kind of RANK() implementation on my table but it's not working properly.
The original query was:
RANK() OVER (PARTITION BY visits.id_partner ORDER BY visits.updated_at DESC) AS rank
My implementation for MariaDB/MySQL:
SELECT
...,
(
CASE visits.id_partner
WHEN #currId THEN
#curRow := #curRow + 1
ELSE
#curRow := 1 AND #currId := visits.id_partner
END
) AS rank
FROM
records rec
JOIN visits ON visits.id = rec.id_visit,
(
SELECT
#curRank := 0,
#currId := NULL
) r
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
I want to select row ranked by id_partner order by updated_at field. When id_partner is same as on row before RANK should increase by 1. When is different than before, it should reset to 1.
But my query is not working at all. I have still rank 1 on all rows. Can you help me find mistake?
Thank you for help!
It is tricky to use variables in MySQL/MariaDB. A variable should only be used and assigned in one statement (as you do correctly). However, AND can short-circuit variable assignment.
I use a construct like this for ROW_NUMBER(). RANK() is actually a bit of a pain . . . DENSE_RANK() and ROW_NUMBER() are simpler. However, this seems to be the code that you are aiming for:
SELECT ...,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM records rec JOIN
visits
ON visits.id = rec.id_visit CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC;
EDIT:
In MySQL (and presumably in MariaDB), sometimes variables don't work quite right unless you use a subquery. So, try this:
SELECT . . .,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM (SELECT ...
FROM records rec JOIN
visits
ON visits.id = rec.id_visit
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
) t CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params;
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.