How to calculate median in mysql by using case when - mysql

I have a table named station having a column LAT_N. I am trying to get the median of LAT_N.
So here is my query:
select
case when count(Lat_N)%2=0 then (select (Lat_N from station limit (1,ceil(count(Lat_N)/2))+ select (Lat_N from station limit (1,floor(count(Lat_N)/2)))))
else (Select Lat_N from station limit(1,floor(count(Lat_N)/2)+1))
end
from station;

Try following query:
SELECT avg(tmp1.LAT_N) as median_LAT FROM (
SELECT #rownum:=#rownum+1 as row_number, s.val
FROM station s, (SELECT #rownum:=0) r
ORDER BY s.LAT_N
) as tmp1,
(
SELECT count(*) as total_rows
FROM station s
) as tmp2
AND tmp1.row_number in ( floor((total_rows+1)/2), floor((total_rows+2)/2) );

Related

only max(value) from union by several columns

I want to retrieve one result for each [Group] by the highest Time.
Result of current code:
SELECT [Group], ArticleNumber, max(TimeTrue) as Time
FROM PerformanceOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
UNION
SELECT [Group], ArticleNumber, max(StopTime) as Time
FROM StoppageOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
ORDER BY Time DESC
The result should be only two records (csv):
Group,ArticleNumber,Time
70521-030,,2021-03-15 13:50:15
Pack2,183026,2021-03-15 13:47:39
Hmmm . . . you would seem to want to union all before aggregating:
SELECT [Group], ArticleNumber, max(Time) as Time
FROM ((SELECT [Group], ArticleNumber, TimeTrue as Time
FROM PerformanceOpc
WHERE [Group] IN ('Pack2', '70521-030')
) UNION ALL
(SELECT [Group], ArticleNumber, StopTime as Time
FROM StoppageOpc
WHERE [Group] IN ('Pack2', '70521-030')
)
) g
GROUP BY [Group], ArticleNumber;
This returns one row per group and article, which seems to be what your query is doing.
If you really want only one row per group, then you want ROW_NUMBER() and not aggregation:
SELECT g.*
FROM (SELECT g.*, ROW_NUMBER() OVER (PARTITION BY [Group] ORDER BY time DESC) as seqnum
FROM ((SELECT [Group], ArticleNumber, TimeTrue as Time
FROM PerformanceOpc
WHERE [Group] IN ('Pack2', '70521-030')
) UNION ALL
(SELECT [Group], ArticleNumber, StopTime as Time
FROM StoppageOpc
WHERE [Group] IN ('Pack2', '70521-030')
)
) g
) g
WHERE seqnum = 1;
Try select top 1 with order by in temporal tables and then query them with union
SELECT top 1[Group], ArticleNumber, max(TimeTrue) as Time into #tmp1
FROM PerformanceOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
order by Time desc
SELECT top 1 [Group], ArticleNumber, max(StopTime) as Time into #tmp2
FROM StoppageOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
ORDER BY Time DESC
select * from #tmp1
union
select * from #tmp2
drop table #tmp1
drop table #tmp2

How to write a query to find Median of column in a Table in MySql?

I was trying to solve problem in Hackerrank SQL Practice section and stuck in Problem 'Weather Observation Problem 20'.
To find Median, I though of the following approach:
sub-query to count the lower half of the entries.
sub-query to count the upper half of the entries.
Equate these queries together under a WHERE clause (so that an entry has the same number of entries before and after).
QUERY:
select round(s.lat_n,4)
from station s
where (
select round(count(s.id)/2)-1
from station
) = (
select count(s1.id)
from station s1
where s1.lat_n > s.lat_n
);
PLEASE HELP ME OUT WITH THE OPTIMIZED QUERY.
LINK OF PROBLEM STATEMENT : https://www.hackerrank.com/challenges/weather-observation-station-20/problem
When you sort the values the median will be either exactly in the middle (odd number of rows) or the average of two values around the middle (even number of rows). For these values the following is true:
at least half of all values (including itself) are equal or less
at least half of all values (including itself) are equal or greater
When you find that/those values (let's call them candidates), you will need the average of distinct candidate values.
The abouve can be expressed with the following query:
select round(avg(distinct lat_n), 4) as median_lat_n
from station s
cross join (select count(*) as total from station) t
where t.total <= 2 * (select count(*) from station s1 where s1.lat_n <= s.lat_n)
and t.total <= 2 * (select count(*) from station s1 where s1.lat_n >= s.lat_n)
Note that this is a quite slow solution for big tables.
Select round(lat_n,4) from (select lat_n , rank() over(order by lat_n) as rnk from station )a where rnk = (select round((count(lat_n)+1)/2,0) as c from station ) ;
This worked for me
/* SQL ERVER /
SELECT LATNR
FROM
(
SELECT CAST(ROUND(LAT_N,4) AS DECIMAL(17,4)) LATNR, RANK() OVER( ORDER BY LAT_N ASC) IDX
FROM STATION
) AS F
WHERE IDX IN ( CEILING (CAST((SELECT COUNT() +1 FROM STATION) AS FLOAT) /2 ))
Select round(lat_n,4)
from (select lat_n , rank() over(order by lat_n) as Rnk_Col
from station) as Rnk_Table
where Rnk_Col = (select round((count(lat_n)+1)/2,0)
from station);
I found another solution that is more robust than hardcoding the +1 and more efficient than including an IF statement.
with Tbl as (select row_number() over (order by lat_n) rc, lat_n from station)
select cast(avg(lat_n) as Decimal(15,4))
from tbl
where rc = (select ceiling(max(rc)/2.0) from tbl)
select round(lat_n,4) from
(SELECT lat_n,(ROW_NUMBER() OVER(order BY lat_n) ) as "serial_no"
from station ) AS t1
WHERE t1.serial_no = (
select (count(*)+1)/2
from station
)
I used row_number() to create a new column in a table that gives a serial number to ascending ordered lat_n and then displayed the median lat_n value corresponding to middle value of row_number.
First assigns position for each rows using ROW_NUMBER() window function
Second select the middle row if row count is odd else select the two rows in the middle as described here
Finally compute the average and round to 4th decimal point.
If row count is odd, the where conditions return the middle row
Else if row count is even, the where conditions return the two middle rows.
WITH POSITIONED AS (
SELECT
ROW_NUMBER() OVER (ORDER BY LAT_N ASC) AS ROW_NUM,
LAT_N
FROM STATION
)
SELECT ROUND(AVG(LAT_N), 4)
FROM POSITIONED
WHERE ROW_NUM = CEIL((SELECT COUNT(*) FROM STATION) / 2) OR
ROW_NUM = CEIL((SELECT COUNT(*) FROM STATION) / 2 + 0.1)

Display the city with the shortest and longest name

I'm doing a question where I have been told:
"Query the two cities in the table with the shortest and longest city names, as well as their respective lengths (i.e.: number of characters in the name). If there is more than one smallest or largest city, choose the one that comes first when ordered alphabetically."
I am running the following code, however it keeps saying my answer is wrong:
SELECT CITY, LEN FROM (
SELECT CITY, LENGTH(CITY) AS LEN, ROW_NUMBER() OVER (ORDER BY LEN, CITY ASC) as r
FROM STATION ) as a
WHERE r IN (1, (select count(*) from a))
Why does that not work?
Use ROW_NUMBER() twice to get the city with min and max length:
SELECT t.CITY, t.LEN
FROM (
SELECT CITY, LENGTH(CITY) LEN,
ROW_NUMBER() OVER (ORDER BY LENGTH(CITY), CITY) as rn1,
ROW_NUMBER() OVER (ORDER BY LENGTH(CITY) DESC, CITY) as rn2
FROM STATION
) t
WHERE 1 IN (t.rn1, t.rn2)
See a simplified demo.
You could also use
select a.CITY, t.max_len, b.CITY, t.min_len
from (
SELECT max(LENGTH(CITY)) max_len , min(LENGTH(CITY)) min_len
FROM STATION
) t
INNER JOIN STATION a ON a.LENGTH(CITY) = t.max_len
INNER JOIN STATION b ON b.LENGTH(CITY) = t.min_len
Try it as two separate query and join it by UNION ALL.
(SELECT CITY, LENGTH(CITY)
FROM STATION
ORDER BY LENGTH(CITY) asc, CITY asc
LIMIT 1)
UNION ALL
(SELECT CITY, LENGTH(CITY)
FROM STATION
ORDER BY LENGTH(CITY) desc, CITY asc
LIMIT 1)

How to get the maximum count, and ID from the table SQL

**castID**
nm0000116
nm0000116
nm0000116
nm0000116
nm0000116
nm0634240
nm0634240
nm0798899
This is my table (created as a view). Now I want to list the castID which has the most count (in this case which is nm0000116, and how many occurences/count it has in this table ( should be 5 times) and I'm not quite sure which query to use
try
Select CastId, count(*) countOfCastId
From table
Group By CastId
Having count(*)
= (Select Max(cnt)
From (Select count(*) cnt
From table
Group By CastId) z)
SELECT
MAX(E),
castId
FROM
(SELECT COUNT(castId) AS E,castId FROM [directors winning movies list] GROUP BY castId) AS t
You could just return the topmost count using LIMIT:
SELECT castID,
COUNT(*) AS Cnt
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
LIMIT 1
;
However, if there can be ties, the above query would return only one row. If you want all the "winners", you could take the count from the above query as a scalar result and compare it against all the counts to return only those that match:
SELECT castID,
COUNT(*) AS Cnt
FROM atable
GROUP BY castID
HAVING COUNT(*) = (
SELECT COUNT(*)
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
LIMIT 1
)
;
(Basically, same as Charles Bretana's approach, it just derives the top count differently.)
Alternatively, you could use a variable to rank all the counts and then return only those that have the ranking of 1:
SELECT castID,
Cnt
FROM (
SELECT castID,
COUNT(*) AS Cnt,
#r := IFNULL(#r, 0) + 1 AS r
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
) AS s
WHERE r = 1
;
Please note that with the above method the variable must either not exist or be pre-initialised with a 0 or NULL prior to running the query. To be on the safe side, you could initialise the variable directly in your query:
SELECT s.castID,
s.Cnt
FROM (SELECT #r := 0) AS x
CROSS JOIN
(
SELECT castID,
COUNT(*) AS Cnt,
#r := #r + 1 AS r
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
) AS s
WHERE s.r = 1
;

how limit start value come from other table in mysql

I have following my sql query, I want to take limit start index from the other table, How could I do it?
SELECT std.totalmarks
FROM student as std
WHERE std.status=1
ORDER BY (std.datetime) ASC
LIMIT (
SELECT us.startnum
FROM user AS us
WHERE us.username='abc'
),10
select q.totalmarks from
(
SELECT *,#curRow := #curRow + 1 AS row_number
FROM student as std JOIN (SELECT #curRow := 0) r
WHERE std.status=1
ORDER BY (std.datetime) ASC
) q
where row_number>(
SELECT us.startnum
FROM user AS us
WHERE us.username='abc'
)
limit 10
select * from
(SELECT std.totalmarks, numstart.startnum, #n:=#n+1 as number
FROM student as std,
(SELECT us.startnum
FROM user AS us
WHERE us.username='abc') as numstart,
(SELECT #n:=0) sess_var
WHERE std.status=1
ORDER BY (std.datetime) ASC) res
where number>=startnum
LIMIT 0,10