查询特定ID时,按顺序查找上一行/下一行

时间:2012-02-29 01:07:06

标签: sql postgresql postgresql-9.1

我有一个表格(简化为极端以使其更清晰)

create table mytable (
  id integer not null,
  owner text not null,
  order_field_1 integer not null,
  order_field_2 integer not null
)

每次从数据库中获取一行时,我都试图获取下一个和前一个元素的ID,以允许导航。这些行不是按ID排序,而是按ORDER BY order_field_1 DESC, order_field_2 DESC排序。

获取所有者的最后一个条目时,使用窗口和超前/滞后来找到我想要的内容是没有问题的

SELECT
  id,
  owner,
  lag(id) over w AS previous_id,
  lead(id) over w AS next_id
FROM
  mytable
WHERE
  owner = 'someuser'
WINDOW w AS (
  ORDER BY order_field_1 DESC, 
  order_field_2 DESC
)
ORDER BY 
  order_field_1 DESC, 
  order_field_2 DESC
LIMIT
  5

这是从记忆中写出的,但这是它的要点,而且效果很好。

我的问题是,当我想使用所有者AND id获取特定行时,我仍然想要找到上一个和下一个id,我不能再使用窗口函数了,因为where只返回一行,我当前做一个子查询以获得两个导航ID的解决方案并不是很好的性能

例如(我只放了以前的id,因为下一个是相同的)

SELECT
  m1.id,
  m1.owner,
  (
    SELECT 
      m2.id 
    FROM 
      mytable m2 
    WHERE 
      m2.owner = m1.owner 
      AND m2.id != m1.id 
      AND (
        m2.order_field_1 < m1.order_field_1 
        OR (
          m2.order_field_1 = m1.order_field_1 
          AND m2.order_field_2 <= m1.order_field_2
        ) 
      ORDER BY 
        m2.order_field_1 DESC, 
        m2.order_field_2 DESC
      LIMIT
        1
  ) AS previous_id
FROM
  mytable m1
WHERE
  owner = 'someuser'
  AND id = 12345

所以我选择了我的行,然后从同一个用户中选择第一行,使用不同的id,即使用较低的order_field_1或相同但较低的order_field_2。

这不是很有效率,我的表现很糟糕,我想知道是否有人对如何改进它有任何想法?

例子数据集:

id |    owner | order_field_1 | order_field_2
 1 | someuser |             4 |             2
 2 | someuser |             2 |             8
 3 | someuser |             4 |             3
 4 | someuser |             3 |             2
 5 | someuser |             4 |             6
 6 | someuser |             4 |             5

序:

id |    owner | order_field_1 | order_field_2
 5 | someuser |             4 |             6
 6 | someuser |             4 |             5
 3 | someuser |             4 |             3
 1 | someuser |             4 |             2
 4 | someuser |             3 |             2
 2 | someuser |             2 |             8

如果我选择owner ='someuser'且id = 3,则previous_id应为1,next_id应为6。

如果我选择owner ='someuser'且id = 1,则previous_id应为4,next_id应为3。

提前感谢您提供任何帮助

2 个答案:

答案 0 :(得分:2)

带窗口功能和CTE

在CTE中WHERE owner = 'someuser'已经了:

WITH t AS (
    SELECT id
          ,owner
          ,lag(id)  over w AS previous_id
          ,lead(id) over w AS next_id
    FROM   mytable
    WHERE  owner = 'someuser'
    WINDOW w AS (ORDER BY order_field_1 DESC, order_field_2 DESC)
    )
SELECT *
FROM   t
WHERE  id = 3

此外,由于您只选择一行,因此最终ORDER BY中无需SELECT

__

带有子查询的老派

它相当丑陋,但如果每个owner很多行可能会更快。你必须测试......

SELECT id
     , owner
     ,(SELECT id
       FROM   tbl p
       WHERE  p.owner = t.owner -- same owner
       AND    p.id   <> t.id    -- different id
       AND    p.order_field_1 <= t.order_field_1
       AND    p.order_field_2 <= t.order_field_2
       ORDER  BY order_field_1 DESC
               , order_field_2 DESC
       LIMIT  1) AS previous_id

     ,(SELECT id
       FROM   tbl n
       WHERE  n.owner = t.owner
       AND    n.id   <> t.id
       AND    n.order_field_1 >= t.order_field_1
       AND    n.order_field_2 >= t.order_field_2
       ORDER  BY order_field_1
               , order_field_2
       LIMIT  1) AS next_id
FROM   tbl t
WHERE  owner = 'someuser'
AND    id = 3

这个也适用于旧版本的PostgreSQL 当然,性能的关键是适当的索引。

答案 1 :(得分:1)

在应用WHERE子句之前如何找到滞后和超前值

WITH T as (
  SELECT
    id,
    owner,
    lag(id) over w AS previous_id,
    lead(id) over w AS next_id
  FROM
    mytable
  WINDOW w AS (
    ORDER BY order_field_1 DESC, 
    order_field_2 DESC
  )
)
  SELECT * FROM T
  WHERE
    owner = 'someuser' AND id = 3
  ORDER BY 
    order_field_1 DESC, 
    order_field_2 DESC