0

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

样品表:

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

4 回答 4

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 的解决方案实现)。

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

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

于 2013-09-26T18:54:16.043 回答
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
于 2013-09-26T18:29:02.307 回答
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
于 2013-09-26T18:57:13.927 回答
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() 用于没有它们的平台。它也可以通过改变一个常数来适应 <> 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 中不起作用)

于 2013-09-26T22:24:09.527 回答