3

我有一张表格,例如(简化到极致以使其更清晰)

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

这是从记忆中写出来的,但这就是它的要点,它工作得很好。

My problem is when I want to get a specific row, using owner AND id, yet I still want to find the previous and next ids, I can not use a window function anymore since only one row is returned by the where, and my current solution of doing a subquery to get both navigation id is not very good performance wise

For exemple (I only put previous id since it's the same for next)

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

So I'm selecting my row, then selection the first row from the same user, with a different id, that is either with a lower order_field_1 or the same but a lower order_field_2.

This is not really efficient and I am getting poor performances, and I'm wondering if anyone has any idea on how I could improve it ?

Exemple dataset:

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

Ordered:

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

If I select owner = 'someuser' and id = 3, previous_id should be 1, next_id should be 6.

If I select owner = 'someuser' and id = 1, previous_id should be 4, next_id should be 3.

Thanks in advance for any help

4

2 回答 2

2

With window functions and CTE

It is much cheaper to have the WHERE owner = 'someuser' in the CTE already:

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

Also, as you only select a single row, there is no need for an ORDER BY in the final SELECT.

__

Old school with subqueries

It's rather ugly, but it might be faster if there are a lot of rows per owner. You'll have to test ...

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

This one works for older versions of PostgreSQL, too.
The key to performance are proper indexes, of course.

于 2012-02-29T04:43:05.097 回答
1

How about finding the lag and lead values before applying the WHERE clause?

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
于 2012-02-29T02:39:31.137 回答