【问题标题】:PostgreSQL Circular IndexPostgreSQL 循环索引
【发布时间】:2018-12-12 12:46:18
【问题描述】:

我有一个简单的问题:“如何处理循环索引或引用”

主要思想是: 弄清楚你是否有一些数据,比如字符串中的位置,并且你有很多字符串。

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]}

每个字符串中的项目可能有重叠,并且开始和结束位置不太重要(只是为了保持项目顺序,例如 ba 之后和 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]

但是,如果查询是项目c,我如何从String A 检索项目a

如果我为项目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 的项目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)

【问题讨论】:

  • 对不起,我不明白你的用例。也许你可以换一种方式解释?查看您的表结构以及数据的存储方式会很有帮助。请添加一些示例数据和预期输出。
  • @S-Man 谢谢,我添加了表格、查询、输出和预期输出的示例。

标签: postgresql indexing circular-reference circular-list


【解决方案1】:

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。我通过使用window function 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
)

【讨论】:

  • 是的,它有帮助。但是第一个解决方案似乎仅限于最近的邻居项目,第二个解决方案似乎不灵活,但对于最终用户的想法是根据需要选择多少个邻居项目,这意味着有时只有 1 个邻居,有时 10 个邻居(不是允许的例子)。谢谢。
猜你喜欢
  • 2012-11-23
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 2010-12-06
  • 2023-02-26
  • 2016-06-01
  • 2012-09-27
  • 2013-01-30
相关资源
最近更新 更多