一,問題提出
問題:已知學生成績表su,包含id,score兩個字段,現需要取出成績第二高的記錄。
建表語句:
-- oracle
CREATE TABLE sc (
id NUMBER,
score NUMBER
)
-- mysql
CREATE TABLE `sc` (
`id` INT(11) DEFAULT NULL,
`score` INT(11) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=gbk
`sc`
數據插入:
--oracle
INSERT INTO sc VALUES ('1', '43');
INSERT INTO sc VALUES ('2', '77');
INSERT INTO sc VALUES ('3', '86');
INSERT INTO sc VALUES ('4', '66');
INSERT INTO sc VALUES ('5', '25');
INSERT INTO sc VALUES ('6', '46');
INSERT INTO sc VALUES ('7', '77');
--mysql
INSERT INTO `sc` VALUES ('1', '43');
INSERT INTO `sc` VALUES ('2', '77');
INSERT INTO `sc` VALUES ('3', '86');
INSERT INTO `sc` VALUES ('4', '66');
INSERT INTO `sc` VALUES ('5', '25');
INSERT INTO `sc` VALUES ('6', '46');
INSERT INTO `sc` VALUES ('7', '77');
二,求解方法
1,在Oracle中
這種問題,對於Oracle來說是小菜一碟,使用排名的分析函數就可以解決,但是要注意考慮有多個第K大的情況。 排名相關的分析函數有三個row_number、rank、dense_rank。這三個函數的使用情況並不相同,簡單說下,詳細用法請自度娘。
1,row_number:返回連續的排序,無論值是否相等。
2,rank:具有相等值得行排序相同,序數值隨後跳躍。
3,dense_rank:具有相等值得行排序相同,序號是連續的。
舉個例子:
row_number : 對於相同的77,採用連續的序號。
id score rn
3 86 1
7 77 2
2 77 3
4 66 4
6 46 5
1 43 6
5 25 7
rank:對於相同的77,採用相同的序號2,但是66的序號並不是3,而是4。這是因爲rank的序號是跳躍,第二個77的序號其實是3的,也就是序號跳了一位。
id score rn
3 86 1
7 77 2
2 77 2
4 66 4
6 46 5
1 43 6
5 25 7
dense_rank: 對於相同的77,採用相同的序號2,但是序號不會因爲相同數據而進行跳躍,所以66的序號是3。
id score rn
3 86 1
7 77 2
2 77 2
4 66 3
6 46 4
1 43 5
5 25 6
針對於考慮重複值的第K大問題,應該使用dense_rank,如果採用rank,則序號就會有跳躍,結果不正確。
完整sql代碼:
select
t.id,
t.score
from (
select
ID,
score,
DENSE_RANK() over(order by score desc ) rn
from sc
) t
where t.rn=2 --第二大,所以取序號爲2的
2,在MySQL中
對於MySQL,如果MySQL的版本<8.0,是使用不了Oracle中的三個排序分析函數的。所以就要有一個通用的方法來解決這種類似的問題,畢竟每種數據庫所包含的函數是不同的,函數不一定通用,但是原始的SQL語法應該是一致的。
在MySQL中的方法中,最先想到的應該是類似以下這種寫法(網上最多的):
SELECT id, MAX(score) score
FROM sc
WHERE score < (SELECT MAX(score) FROM sc )
看上去很簡潔,但是有兩個問題:
1,查詢出來的id,其實並不是我們所想的那樣是最大score這條記錄對應的id。大家可以動手運行下,我最開始也是這麼認爲。
2,對於用重複情況時,這條語句沒法全部查詢出來。
3,通用的語句
那麼有沒有針對兩個數據庫,並且都滿足要求的語句呢?方法應該有許多,以下語句大家可以參考下:
SELECT
*
FROM
sc
WHERE score =
(SELECT
t1.score
FROM sc t1
JOIN sc t2 ON t1.score <= t2.score
GROUP BY t1.score
HAVING COUNT(DISTINCT t2.score) = 2) -- 以取第二大的爲例子
如果有更好的方法,評論留言!!