Циркулярный указатель PostgreSQL - PullRequest
0 голосов
/ 12 декабря 2018

У меня простой вопрос: «Как обращаться с круговым индексом или ссылкой»

Основная идея: выяснить, есть ли у вас какие-либо данные, например позиции в строке, и много ли у вас строк.

String N = {n1 : [start1, end1], n2 : [start2, end2], ..., nn : [startn, endn]}

String A = {a : [1, 10], b : [15, 20], c : [21, 50]}
String B = {a : [52, 8], b : [10, 20], c : [21, 55]}

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

Я строю просто еще один столбец, чтобы назначить другое элементам в каждой строке.

String N = {n1 : [start1, end1, index1], n2 : [start2, end2, index2], ..., nn : [startn, endn, indexnn]}

String A = {a : [1, 10, 1], b : [15, 20, 2], c : [21, 50, 3]}
String B = {a : [52, 8, 1], b : [10, 20, 2], c : [21, 55, 3]}

Идея такова: иногда мне нужно запрашивать другие важные элементы, поэтому я делаю некоторые запросычтобы получить все элементы между 2 вперед и 2 назад в запросе.

В настоящее время запрос очень прост:

SELECT * 
FROM strings 
WHERE string = 'A' 
    AND index BETWEEN (
                  SELECT index 
                  FROM strings 
                  WHERE string = 'A' AND item = b
              ) - 1 AND (
                  SELECT index 
                  FROM strings 
                  WHERE string = 'A' AND item = b
              ) + 1;

[* или что-то лучше этого] И он вернет элементы [a, b, c]

Но как мне получить элемент a из String A, если запрос будет элементом c?

Если я сделаю для элемента c:

SELECT * 
FROM strings 
WHERE string = 'A' 
    AND index BETWEEN (
                  SELECT index 
                  FROM strings 
                  WHERE string = 'A' AND item = c
              ) - 1 AND (
                  SELECT index 
                  FROM strings 
                  WHERE string = 'A' AND item = c
              ) + 1;

Он мне не вернет [b, c, a], просто вернет [b, c].

Заранее спасибо

ПРИМЕР:

Таблица

CREATE TEMP TABLE strings (
     string_name VARCHAR, 
     item VARCHAR, 
     s_start INTEGER, 
     s_end INTEGER, 
     idx INTEGER
);

Данные

INSERT INTO strings VALUES 
('a', 'a1', 10, 20, 1),
('a', 'a2', 10, 20, 2),
('a', 'a3', 10, 20, 3),
('a', 'a4', 10, 20, 4),
('b', 'b1', 1, 20, 1),
('b', 'b2', 10, 20, 2),
('b', 'a3', 10, 20, 3),
('b', 'c4', 10, 20, 4);

Общий запрос

WITH myvar as (
    SELECT idx as s_idx 
    FROM strings 
    WHERE string_name = 'b' AND item = 'a3'
) 
SELECT * 
FROM strings AS s 
JOIN myvar 
ON true 
WHERE string_name = 'b' 
    AND idx BETWEEN s_idx -1 AND s_idx + 1;

вывод:

 string_name | item | s_start | s_end | idx | s_idx  
-------------+------+---------+-------+-----+-------  
 b           | b2   |      10 |    20 |   2 |     3  
 b           | a3   |      10 |    20 |   3 |     3  
 b           | c4   |      10 |    20 |   4 |     3  
(3 rows)  

Проблемный запрос (когда idx больше или меньше от строки как элемент c4 от String B, который является последним idx)

WITH myvar as ( 
    SELECT idx as s_idx 
    FROM strings 
    WHERE string_name = 'b' 
         AND item = 'c4'
) 
SELECT * 
FROM strings AS s 
JOIN myvar 
ON true
WHERE string_name = 'b' 
    AND idx BETWEEN s_idx -1 AND s_idx + 1;

Выход

 string_name | item | s_start | s_end | idx | s_idx 
-------------+------+---------+-------+-----+-------
 b           | a3   |      10 |    20 |   3 |     4
 b           | c4   |      10 |    20 |   4 |     4
(2 rows)

Ожидаемый выход

 string_name | item | s_start | s_end | idx | s_idx 
-------------+------+---------+-------+-----+-------
 b           | a3   |      10 |    20 |   3 |     4
 b           | c4   |      10 |    20 |   4 |     4
 b           | b1   |       1 |    20 |   1 |     4
(2 rows)

1 Ответ

0 голосов
/ 12 декабря 2018

demo: db <> fiddle

WITH myvar as (
    SELECT 
        CASE WHEN idx = 1 THEN max_idx ELSE idx - 1 END as prev_idx, -- 2
        idx as s_idx,
        CASE WHEN idx = max_idx THEN 1 ELSE idx + 1 END as next_idx
    FROM (
        SELECT 
            *, 
            MAX(idx) OVER (PARTITION BY string_name) as max_idx      -- 1
        FROM strings 
        WHERE string_name = 'b'
    ) s
    WHERE item = 'c4'
) 
SELECT s.* 
FROM strings AS s 
JOIN myvar 
ON true 
WHERE string_name = 'b' 
    AND idx = ANY (ARRAY[prev_idx, s_idx, next_idx])                 -- 3
  1. Получите максимум idx на строку.Я получил это с помощью оконной функции MAX
  2. Теперь я могу проверить, следует ли свернуть предыдущий idx до последнего (если текущий idx является первым) или следующий idx должен быть свернут первым (если текущий последний).
  3. Я не использовал BETWEEN, потому что в вашем случае это приводит к серьезным проблемам.Потому что 4,3,1 приведет к BETWEEN 1 AND 4, что также даст 2.Итак, я сделал массив из этих трех значений, но есть много других способов (например, подзапрос вместо CTE)

Если у вас будут большие диапазоны, такие как [-3, +3]этот путь может быть очень неприятным.В этом случае я бы попробовал что-то по модулю:

demo: db <> dbfiddle

WITH myvar as (
    SELECT *
    FROM (
        SELECT 
            idx as s_idx, 
            item, 
            MAX(idx) OVER (PARTITION BY string_name) + 1 as max_idx
        FROM strings 
        WHERE string_name = 'b'
    )s
    WHERE item = 'g7'
) 
SELECT 
    s.*
FROM strings AS s 
JOIN myvar 
ON true 
WHERE string_name = 'b' 
    AND idx = ANY (ARRAY[
        (s_idx - 3) % max_idx,
        (s_idx - 2) % max_idx,
        (s_idx - 1) % max_idx,
        s_idx,
        (s_idx + 1) % max_idx,
        (s_idx + 2) % max_idx,
        (s_idx + 3) % max_idx
    ])

Часть массива также может быть сгенерирована с помощью generate_series.Так что он более гибок для разных диапазонов:

... AND idx IN (
    SELECT (s_idx + gs) % max_idx 
    FROM myvar, generate_series(-3, 3) gs
)
...