Как я могу выбрать соседние строки в произвольную строку (в sql или postgresql)? - PullRequest
18 голосов
/ 09 сентября 2010

Я хочу выбрать несколько строк на основе определенных критериев, а затем взять одну запись из этого набора и 5 строк до него и после него.

Теперь я могу сделать это численно, если в таблице есть первичный ключ (например, первичные ключи, которые численно на 5 меньше, чем ключ целевой строки и на 5 больше, чем ключ целевой строки).

Итак, выберите строку с первичным ключом 7 и соседние строки:

select primary_key from table where primary_key > (7-5) order by primary_key limit 11;

2
3
4
5
6
-=7=-
8
9
10
11
12

Но если я выберу только определенные строки для начала, я потеряю этот числовой метод использования первичных ключей (и это предполагало, что у ключей не было пробелов в их порядке в любом случае), и мне нужен другой способ получить ближайший строки до и после определенной целевой строки.

Вывод первичного ключа такого выбора может выглядеть более случайным и, следовательно, менее успешным для математического определения местоположения (поскольку некоторые результаты будут отфильтрованы, например, с помощью where active=1):

select primary_key from table where primary_key > (34-5) 
    order by primary_key where active=1 limit 11;

30
-=34=-
80
83
100
113
125
126
127
128
129

Обратите внимание, что из-за пробелов в первичных ключах, вызванных примером, в котором условие ( например, потому что есть много неактивных элементов ), я больше не получаю самые близкие 5 выше и 5 ниже, вместо этого я получаю самое близкое 1 ниже и самое близкое 9 выше.

Ответы [ 5 ]

22 голосов
/ 09 сентября 2010

Есть много способов сделать это, если вы выполняете два запроса на языке программирования, но вот один из способов сделать это в одном запросе SQL:

(SELECT * FROM table WHERE id >= 34 AND active = 1 ORDER BY id ASC LIMIT 6)
UNION
(SELECT * FROM table WHERE id < 34 AND active = 1 ORDER BY id DESC LIMIT 5)
ORDER BY id ASC

Это вернет 5 строк выше, целевую строку и 5 строк ниже.

7 голосов
/ 09 сентября 2010

Вот еще один способ сделать это с опережением и отставанием аналитических функций.Было бы хорошо, если бы мы могли использовать аналитические функции в предложении WHERE.Поэтому вместо этого вам нужно использовать подзапросы или CTE.Вот пример, который будет работать с образцом базы данных pagila.

WITH base AS (
    SELECT lag(customer_id, 5) OVER (ORDER BY customer_id) lag, 
      lead(customer_id, 5) OVER (ORDER BY customer_id) lead, 
      c.*
    FROM customer c
    WHERE c.active = 1
    AND c.last_name LIKE 'B%'
) 
SELECT base.* FROM base 
JOIN (
  -- Select the center row, coalesce so it still works if there aren't 
  -- 5 rows in front or behind
  SELECT COALESCE(lag, 0) AS lag, COALESCE(lead, 99999) AS lead 
  FROM base WHERE customer_id = 280
) sub ON base.customer_id BETWEEN sub.lag AND sub.lead

Проблема с решением sgriffinusa состоит в том, что вы не знаете, каким row_number будет ваш центральный ряд.Он предположил, что это будет строка 30.

1 голос
/ 16 апреля 2014

Для аналогичного запроса я использую аналитические функции без CTE. Что-то вроде:

select ..., LEAD(gm.id) OVER (ORDER BY Cit DESC) as leadId, LEAD(gm.id, 2) OVER (ORDER BY Cit DESC) as leadId2, LAG(gm.id) OVER (ORDER BY Cit DESC) as lagId, LAG(gm.id, 2) OVER (ORDER BY Cit DESC) as lagId2 ... where id = 25912 or leadId = 25912 or leadId2 = 25912 or lagId = 25912 or lagId2 = 25912

такой запрос работает для меня быстрее, чем CTE с объединением (ответ от Скотта Бэйли). Но, конечно, менее элегантный

0 голосов
/ 09 сентября 2010

Если вы хотите сделать это «реляционно чистым» способом, вы можете написать запрос, который сортирует и нумерует строки. Как:

select (
  select count(*) from employees b
  where b.name < a.name
) as idx, name
from employees a
order by name

Затем используйте это как общее табличное выражение. Напишите выборку, которая фильтрует его по интересующим вас строкам, затем соедините его обратно с самим собой, используя критерий того, что индекс правой копии таблицы не более чем на k больше или меньше, чем индекс строка слева. Проецируйте только строки справа. Как:

with numbered_emps as (
  select (
    select count(*)
    from employees b
    where b.name < a.name
  ) as idx, name
  from employees a
  order by name
)
select b.*
from numbered_emps a, numbered_emps b
where a.name like '% Smith' -- this is your main selection criterion
and ((b.idx - a.idx) between -5 and 5) -- this is your adjacency fuzzy-join criterion

Что может быть проще!

Я бы предположил, что решения на основе номеров строк будут быстрее.

0 голосов
/ 09 сентября 2010

Вы можете сделать это, используя row_number () (доступно с 8.4). Возможно, это неправильный синтаксис (не знаком с postgresql), но, надеюсь, идея будет проиллюстрирована:

SELECT *
FROM (SELECT ROW_NUMBER() OVER (ORDER BY primary_key) AS r, *
      FROM table
      WHERE active=1) t
WHERE 25 < r and r < 35

Это создаст первый столбец с порядковыми номерами. Вы можете использовать это для идентификации отдельной строки и строк над и под ней.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...