I want to find the person with the 22nd highest salary - mysql

I am using MySQL, I have 50 records in employee table. I want to find the person with the 22nd highest salary.

Use LIMIT, specifying both an offset and a row count.
To get the 22nd ranked person in order of highest salary, do:
SELECT person
FROM employee
ORDER BY salary DESC
LIMIT 21, 1
Notice the use of 21 here. This is because the offset of the initial row (1st highest salary) is actually 0. Therefore the 22nd highest salary will actually be an offset of 21 (the 21st row in 0-based counting, or "skip 21 rows").
To get the person(s) with the 22nd highest salary, you will need one more level of indirection. Try:
SELECT person
FROM employee
WHERE salary = (
SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 21, 1
)

here's another one, considering you have duplicate salary number. I guess limit won't correct solve your case if you have some duplicates. Try something like this,
SELECT aa.*
FROM table1 aa
INNER JOIN
(
SELECT #row:=#row+1 rankNo,
a.Salary
FROM (SELECT DISTINCT Salary FROM table1) a,
(SELECT #row:=0) s
ORDER BY Salary DESC
) bb ON aa.Salary = bb.Salary AND
bb.rankNo = 2
SQLFiddle Demo
consider you have records like this,
CREATE TABLE Table1
(`EmpID` int, `Salary` int);
INSERT INTO Table1
(`EmpID`, `Salary`)
VALUES
(1, 10),
(2, 12), -- duplicate
(3, 11),
(4, 12), -- duplicate
(5, 14),
(6, 12); -- duplicate
╔═══════╦════════╗
║ EMPID ║ SALARY ║
╠═══════╬════════╣
║ 1 ║ 10 ║
║ 2 ║ 12 ║
║ 3 ║ 11 ║ -- you want to get this value (*2nd from the last value*)
║ 4 ║ 12 ║
║ 5 ║ 14 ║
║ 6 ║ 12 ║
╚═══════╩════════╝

SELECT MIN(salary) FROM (
SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 22
) limited_salary

Just answered a similar question here: select all rows except the four most recent
In your case, you'll want to LIMIT to 1, and OFFSET to the 22 position.

LIMIT 21,1
How to find n'th highest value of a column?
Query to find nth max value of a column

Related

MySQL query to group by and then take difference based on criteria instead of sum

Here is my table
Type Amount Year Month
P 10 2016 1
P 15 2016 2
T 5 2016 1
T 20 2016 3
There will only be one month-year combination for P and T. In other words, there cannot be two rows with P having the same Year and Month combination.
Here's what I'd like to do
Get the difference in amount (amount in P row minus amount in T row) where month and year are the same. If there is no corresponding P or T row, then assume amount is 0. So the output would be
Amount Year Month
5 2016 1
15 2016 2
-20 2016 3
What is the simplest way of doing this (by simple, I mean least amount of code repetition). One way I can think of is to first get the P and T rows into two separate derived tables (using select statements), then do a left join union with a right join, but this involves a lot of code repetition.
You could use conditional aggregation:
SELECT `Year`, `Month`,
SUM(CASE WHEN `Type` = 'P' THEN `Amount` ELSE -`Amount` END) AS Amount
FROM tab
GROUP BY `Year`, `Month`;
Output:
╔══════╦═══════╦════════╗
║ Year ║ Month ║ Amount ║
╠══════╬═══════╬════════╣
║ 2016 ║ 1 ║ 5 ║
║ 2016 ║ 2 ║ 15 ║
║ 2016 ║ 3 ║ -20 ║
╚══════╩═══════╩════════╝
LiveDemo
Using IF function:
SELECT `Year`, `Month`,
SUM(IF(`Type` = 'P', `Amount`, -`Amount`)) AS Amount
FROM tab
GROUP BY `Year`, `Month`;
Another way is to use UNION ALL:
SELECT `Year`, `Month`, SUM(`Amount`) AS Amount
FROM (SELECT `Year`, `Month`, `Amount`
FROM tab
WHERE `Type` = 'P'
UNION ALL
SELECT `Year`, `Month`, -`Amount`
FROM tab
WHERE `Type` = 'T') AS sub
GROUP BY `Year`, `Month`;
LiveDemo2

Fetch name when number of distinct names less than 200 mysql

I have a table whose structure is as follows
╔════════╦══════════╦════════════╗
║ app_id ║ app_name ║ categoryId ║
╠════════╬══════════╬════════════╣
║ 1200 ║ A ║ B ║
║ 1200 ║ A ║ C ║
║ 1200 ║ A ║ D ║
║ 1201 ║ E ║ F ║
║ 1201 ║ E ║ G ║
╚════════╩══════════╩════════════╝
Now I have 1600 such data. What I am trying to get is, a query to fetch data till the number of distinct appids reaches 200. After 200 is reached it should no longer fetch the rest of the data. I tried count(distinct(app_id)) but that doesn't seem to work the way I think. I am really struck here and it is a major performance issue. Any help is much appreciated. Thanks in advance.
You can try like this:
SELECT app_id
FROM myTable
GROUP BY app_id
HAVING COUNT(distinct app_id) < 200;
If what you want is
a query to fetch data till the number of distinct appids reaches 200
then you can use variables to implement this:
SELECT app_id, app_name, categoryId
FROM (
SELECT app_id, app_name, categoryId,
#appIDcnt := IF (#appID = app_id,
IF(#appID := app_id, #appIDcnt, #appIDcnt),
IF(#appID := app_id, #appIDcnt+1, #appIDcnt+1)) AS cnt
FROM mytable
CROSS JOIN (SELECT #appID := 0, #appIDcnt := 0) AS vars
ORDER BY app_id ) AS t
WHERE t.cnt <= 200
In the above query #appIDcnt variable is used to count distinct app_id values. The query fetches table data until #appIDcnt value reaches 200.
Demo here
use limit it will return the first 200 results only.
SELECT DISTINCT ap_id FROM appTable LIMIT 200
and then if you need to get the next 200 then use offset 200 which will start with row 201
SELECT DISTINCT ap_id FROM ap_Table LIMIT 200 OFFSET 200

select distinct along with other rows outer / join?

I have a table similar to
╔═════════════════════════════════════╗
║ id type report_id state ║
╠═════════════════════════════════════╣
║ 1 dc 122 pending ║
║ 2 dc 123 pending ║
║ 3 dc 123 approved ║
║ 4 dc 123 pending ║
║ 5 dc 123 approved ║
║ 6 bb 123 pending ║
║ 7 dc 123 pending ║
║ 8 dc 124 pending ║
║ 9 dc 125 pending ║
╚═════════════════════════════════════╝
I am trying to fetch the last report_id by type where state is pending
so results I would want are
122 (id 1 -- dc)
123 (id 6 -- bb)
123 (id 7 -- dc)
124 (id 8 -- dc)
125 (id 9 -- dc)
Right now I am fetching all, then running 3 loops on the array... I know there is a way to either join or perform an outer query to get it without the loops, any input on how i would word it?
I ran a query on a test database (15 records) and it worked there, but hangs on the live database (40k records)
SELECT report_id, type, state
FROM histories
WHERE id IN (
SELECT MAX(id) FROM histories
GROUP BY report_id
)
AND state <> 'approved'
For this, the sub-query executes fine and gives me proper results, the main query will give me results if I extract the sub-query, but combine them and it hangs...
Tested on ta SQL (not MySQL) database on a local machine with a limited data-set
I think you just want a group by with a filter:
select report_id, type, max(id)
from table t
where state = 'pending'
group by report_id, type;
Something like
SELECT report_id, MAX(id), type
FROM table_name
WHERE state = 'pending'
GROUP BY report_id, type
ORDER BY report_id
where table_name is your tables name. It is difficult to tell what you want from the results in your question.
What I did was this
take the sub-query and run it
SELECT MAX(id) FROM histories
GROUP BY report_id
... store as $id_array
then take the result and store it as CSV
$id_csv = implode(', ' $id_array);
then run my primary query
SELECT report_id, type, state
FROM histories
WHERE id IN ($id_csv)
AND state <> 'approved'
Not sure why it made a difference separating it, but it did the job (query takes about 3 seconds to run, I wish it were faster)

Select a record just if the one before it has a lower value

I work with mysql and I've never encountered such a great challenge.
I hope you can help..
I have 1 table called Reports:
ID SerialNumber Remain_Toner_Black
7099 Z5UEBJAC900002Y 37
7281 Z5UEBJAC900002Y 36
7331 Z5UEBJAC900002Y 100
7627 8Z37B1DQ100105N 58
7660 8Z37B1DQ100105N 57
5996 CND8DDM2FH 83
5971 CND8DDM2FH 83
7062 3960125290 0
7088 3960125290 93
7100 3960125290 100
Now I want to be able to select records from the table where Remain_Toner_Black is higher than Remain_Toner_Black of the previous row in the table with the same SerialNumber (by previous I mean lower ID with the same SerialNumber).
For the above records I want the results below:
ID SerialNumber Remain_Toner_Black_Before Remain_Toner_Black_After
7331 Z5UEBJAC900002Y 36 100
7088 3960125290 0 93
7100 3960125290 93 100
SELECT a.ID, a.SerialNumber,
b.Remain_Toner_Black BeforeCount,
a.Remain_Toner_Black AfterCount
FROM
(
SELECT A.ID,
A.SerialNumber,
A.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM TableName a
) a
INNER JOIN
(
SELECT A.ID,
A.SerialNumber,
A.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM TableName a
) b ON a.SerialNumber = b.SerialNumber AND
a.RowNumber = b.RowNumber + 1
WHERE b.Remain_Toner_Black < a.Remain_Toner_Black
SQLFiddle Demo
OUTPUT
╔══════╦═════════════════╦═════════════╦════════════╗
║ ID ║ SERIALNUMBER ║ BEFORECOUNT ║ AFTERCOUNT ║
╠══════╬═════════════════╬═════════════╬════════════╣
║ 7331 ║ Z5UEBJAC900002Y ║ 36 ║ 100 ║
║ 7088 ║ 3960125290 ║ 0 ║ 93 ║
║ 7100 ║ 3960125290 ║ 93 ║ 100 ║
╚══════╩═════════════════╩═════════════╩════════════╝
BRIEF EXPLANATION
What the query above does is it generates a sequential number which mimics ROW_NUMBER() on other RDBS for every SerialNumber ordered by ID in ascending order.
The two subquery is then joined via the SerialNumber and sequential number generated. On the generated number, the value on the first subquery must be equal to plus one of the value on the second subquery to get the number of toner on the next reord.
MySQL (or indeed most other RDBMS) doesn't easily allow cross-row comparisons.
To do this sort of query, you must either perform a very expensive join comparing one version of the table to several rows of another version of the table, or construct a complex expression using user variables.
For example (with user variables):
SELECT ID, SerialNumber, #x Remain_Toner_Before,
#x := Remain_Toner_Black AS Remain_Toner_After
FROM Reports, (SELECT #x := -4) x
WHERE Remain_Toner_Black > #x
AND SerialNumber = '3960125290'
ORDER BY ID;
(the -4 is from your comment to another answer)
A better solution is to do this with cursors or in your application code, where you need only do one pass and compare simple logic by using variables in the cursor or application code.

Top 3 Scores - MySQL [duplicate]

This question already has answers here:
MySQL's alternative to T-SQL's WITH TIES
(3 answers)
Closed 9 years ago.
I'm building a "Top 3 Scores" Leaderboard. I want to display the top three scores, drawing the max per person, but I don't want to limit 3, because I want to display anyone that has the top 3 scores. So for example, with the data below,
+----+-----+
|Name|Score|
+----+-----+
|Matt| 17|
|Mark| 29|
|Luke| 28|
|John| 29|
|Paul| 27|
|Matt| 29|
|Mark| 22|
+----+-----+
I want to display:
+------+-----+
|Name |Score|
+------+-----+
|1.Matt| 30|
|2.Mark| 29|
|2.John| 29|
|3.Luke| 28|
+------+-----+
My first thought is to extract the max for everyone, and then stop displaying after the score changes (using PHP).
select name, max(score)
from SCORES
group by name
order by name
Is there any way to do this directly in SQL?
SELECT name, score
FROM SCORES
JOIN (SELECT distinct score score3
FROM scores
ORDER BY score DESC
LIMIT 2, 1) x
ON score >= score3
ORDER by score DESC
FIDDLE
SELECT name,score
FROM SCORES
WHERE score in (
SELECT distinct s.score
FROM SCORES as s
ORDER BY s.score desc
LIMIT 3)
)
ORDER BY score
SELECT Name, MAX(Score) Score
FROM TableName a
WHERE EXISTS
(
SELECT 1
FROM TableName b
WHERE a.Score = b.Score
GROUP BY Score
ORDER BY Score DESC
LIMIT 3
)
GROUP BY Name
ORDER BY Score DESC
SQLFiddle Demo
OUTPUT based on the records given above
╔══════╦═══════╗
║ NAME ║ SCORE ║
╠══════╬═══════╣
║ Mark ║ 29 ║
║ John ║ 29 ║
║ Matt ║ 29 ║
║ Luke ║ 28 ║
║ Paul ║ 27 ║
╚══════╩═══════╝
this will give the top three scores, regardless of ties.
SELECT score FROM mytable group by score
ORDER BY score DESC
LIMIT 3
now get the third score.
SELECT MIN(score') FROM (SELECTscoreFROMmytablegroup by score
ORDER BYscore` DESC
LIMIT 3) as top3
finally get everything equal or above the third score
SELECT * FROM mytable WHERE score' >=
(SELECT MIN(score') FROM
(SELECT score FROM mtyable group by score'
ORDER BYscore` DESC
LIMIT 3) as top3)
I would try something like:
SELECT * FROM mytable
WHERE score IN (SELECT DISTINCT score FROM mytable ORDER BY score DESC LIMIT 3)
ORDER BY score DESC
Here IS SQL FIDDLE.
SELECT
NAME,
score
FROM SCORES
WHERE FIND_IN_SET(score,(SELECT
SUBSTRING_INDEX(GROUP_CONCAT( DISTINCT score ORDER BY score DESC),',',3)
FROM SCORES)) > 0
ORDER BY score desc