根据行ID查找排序中的行号,然后查找其邻居

时间:2012-05-22 12:39:20

标签: sql sql-order-by firebird rank rowcount

说我有一些SELECT声明:

SELECT id, name FROM people
   ORDER BY name ASC;

我在people表中有几百万行,ORDER BY子句可能比我在这里显示的要复杂得多(可能在十几列上运行)。

我只检索行的一小部分(比如行1..11),以便在UI中显示它们。现在,我想解决以下问题:

  1. 查找具有给定id的行号。
  2. 使用给定的id显示之前的5个项目和连续的5个项目。
  3. 问题2很容易解决,一旦我解决了问题1,因为如果我知道我要查找的项目在排序结果集中有行号1000,我可以使用这样的东西(这是Firebird SQL方言):

    SELECT id, name FROM people
       ORDER BY name ASC
       ROWS 995 TO 1005;
    

    我也知道我可以通过计算我要查找的那一行之前的所有行来找到行的rank,但是这可能导致很长的WHERE条款与吨条件中ORAND的条件。我必须反复这样做。使用我的测试数据,即使使用正确索引的列,这也需要数百毫秒,这太慢了。

    是否有一些方法可以通过使用一些SQL:2003功能(例如Firebird 3.0中支持的row_number)来实现这一目的?我绝不是一个SQL大师,我需要一些指针。我可以创建一个缓存视图,其结果将包括排名/密集排名/行索引吗?

1 个答案:

答案 0 :(得分:3)

Firebird似乎支持窗口功能(在Oracle中称为分析功能)。所以你可以做到以下几点:

查找具有给定ID的行的“行”编号:

select id, row_number() over (partition by NULL order by name, id)
from t
where id = <id>

这假设id是唯一的。

解决第二个问题:

select t.*
from (select id, row_number() over (partition by NULL order by name, id) as rownum
      from t
     ) t join 
     (select id, row_number() over (partition by NULL order by name, id) as rownum
      from t
      where id = <id>
     ) tid
     on t.rownum between tid.rownum - 5 and tid.rownum + 5

但是,如果您可以修改表结构,我可能会提出其他建议。大多数数据库都能够在插入行时添加自动增量列。如果您的记录永远不会被删除,那么这可以作为您的计数器服务器,从而简化您的查询。