有多少种方法可以在SQL搜索中获得第二行?

时间:2013-09-26 17:55:26

标签: mysql sql

假设我正在寻找第二高的记录。

样本表:

CREATE TABLE `my_table` (
    `id` int(2) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) NOT NULL,
    `value` int(10),
    PRIMARY KEY (`id`)
);
INSERT INTO `my_table` (`id`, `name`, `value`) VALUES (NULL, 'foo', '200'), (NULL, 'bar', '100'), (NULL, 'baz', '0'), (NULL, 'quux', '300');

第二高的值是foo。有多少种方法可以得到这个结果?

明显的例子是:

SELECT name FROM my_table ORDER BY value DESC LIMIT 1 OFFSET 1;

您能想到其他例子吗?

我正在尝试这个,但不支持LIMIT & IN/ALL/ANY/SOME subquery

SELECT name FROM my_table WHERE value IN (
    SELECT MIN(value) FROM my_table ORDER BY value DESC LIMIT 1
) LIMIT 1;

4 个答案:

答案 0 :(得分:2)

Eduardo在标准SQL中的解决方案

select *
from (
  select  id, 
          name, 
          value, 
          row_number() over (order by value) as rn
  from my_table t
) t
where rn = 1 -- can pick any row using this

这适用于除MySQL以外的任何现代DBMS。此解决方案通常比使用子选择的解决方案更快。它也可以轻松返回第2行,第3行......(同样,这也可以通过Eduardo的解决方案实现)。

它也可以调整为按组计数(添加partition by),这样就可以用相同的模式解决“每组最大n”问题。

这是一个可以使用的SQLFiddle:http://sqlfiddle.com/#!12/286d0/1

答案 1 :(得分:1)

您可以像这样使用内联初始化:

select * from (
  select  id, 
          name, 
          value, 
          @curRank := @curRank + 1 AS rank 
  from    my_table t, (SELECT @curRank := 0) r
  order by value desc
) tb
where tb.rank = 2

答案 2 :(得分:1)

SELECT name 
FROM my_table 
WHERE value < (SELECT max(value) FROM my_table)
ORDER BY value DESC 
LIMIT 1


SELECT name 
FROM my_table 
WHERE value = (
  SELECT min(r.value)
  FROM (
    SELECT name, value 
    FROM my_table 
    ORDER BY value DESC 
    LIMIT 2
  ) r
)
LIMIT 1

答案 3 :(得分:1)

这仅适用于完全第二高:

SELECT * FROM my_table two
WHERE EXISTS (
        SELECT * FROM my_table one
        WHERE one.value  > two.value
                AND NOT EXISTS (
                SELECT * FROM my_table zero
                WHERE zero.value > one.value
                )
        )
LIMIT 1
        ;

这个模拟了没有它们的平台的窗口函数rank()。它也可以适用于排名&lt;&gt; 2通过改变一个常数:

SELECT one.*
        -- , 1+COALESCE(agg.rnk,0) AS rnk
FROM my_table one
LEFT JOIN (
        SELECT one.id , COUNT(*) AS rnk
        FROM my_table one
        JOIN my_table cnt ON cnt.value > one.value
        GROUP BY one.id
        ) agg ON agg.id = one.id
WHERE  agg.rnk=1 -- the aggregate starts counting at zero
        ;

两个解决方案都需要功能性自联接(我不知道mysql是否允许它们,IIRC只有在表是更新或删除的目标时才会禁止它们)

下面的内容不需要窗口函数,但使用递归查询来枚举排名:

WITH RECURSIVE agg AS (
        SELECT one.id
        , one.value
        , 1 AS rnk
        FROM my_table one
        WHERE NOT EXISTS (
                SELECT * FROM my_table zero
                WHERE zero.value > one.value
                )
        UNION ALL
        SELECT two.id
        , two.value
        , agg.rnk+1 AS rnk
        FROM my_table two
        JOIN agg ON two.value < agg.value
        WHERE NOT EXISTS (
                SELECT * FROM my_table nx
                WHERE nx.value > two.value
                AND nx.value < agg.value
                )
        )
SELECT * FROM agg
WHERE rnk = 2
        ;

(显然,递归查询在mysql中不起作用)