查询在sqlite中查找存储为邻接列表的树中的下一个项目

时间:2014-08-04 06:00:41

标签: sql sqlite hierarchical-data recursive-query adjacency-list

背景:sqlite应该用于存储可以使用SNMP查询的信息。 SNMP以OID的层次结构组织信息,并支持3种类型的查询:

  1. 单个OID的值
  2. OID和给定OID旁边的lexicographically值。
  3. 子树的所有OID和值
  4. 我从下表开始:

    PRAGMA foreign_keys = ON;
    
    CREATE TABLE data (
       oid TEXT NOT NULL PRIMARY KEY,
       parent TEXT REFERENCES data(oid) ON DELETE CASCADE CHECK(oid = '1' OR parent IS NOT NULL),
       leaf_id INTEGER NOT NULL,
       value BLOB DEFAULT NULL,
       CHECK(parent IS NULL OR oid = parent || '.' || leaf_id)
    );
    
    INSERT INTO data VALUES ('1', NULL, 1, NULL);
    INSERT INTO data VALUES ('1.5', '1', 5, NULL);
    INSERT INTO data VALUES ('1.5.2', '1.5', 2, 'foo');
    INSERT INTO data VALUES ('1.5.11', '1.5', 11, 'foo');
    INSERT INTO data VALUES ('1.5.1', '1.5', 1, 'foo');
    INSERT INTO data VALUES ('1.3', '1', 3, NULL);
    INSERT INTO data VALUES ('1.3.4', '1.3', 4, 'foo');
    INSERT INTO data VALUES ('1.3.7', '1.3', 7, 'foo');
    INSERT INTO data VALUES ('1.3.5', '1.3', 5, 'foo');
    INSERT INTO data VALUES ('1.3.6', '1.3', 6, 'foo');
    

    将OID的最后一部分存储为INT的想法是能够按字典顺序排列具有相同父项的所有项目。

    第一种查询类型很容易编写。但是 - 由于我对SQL的经验有限 - 在第二和第三种情况下编写查询时很困难。 我认为应该可以使用正确的WITH RECURSIVE ... SELECT构造。到目前为止,我找不到一种方法来结合3种情况(第一个孩子,下一个兄弟,下一个父母)和正确的排序也不起作用。另一个复杂性是必须忽略值为NULL的所有OID。

    如果有人能提供这两个问题或协助我写这些问题,我将非常感激。

    如果查询过于复杂或无法编写,另一个想法是将另一列“next”添加到下一个项目的“指针”,并使用触发器填充下一个值。

    我不喜欢使用嵌套集 - 对于插入/删除来说太复杂和缓慢。

1 个答案:

答案 0 :(得分:2)

检索子树很简单:

WITH RECURSIVE subtree(oid, value, depth, leaf_id) AS (
    SELECT oid,
           value,
           0 AS depth,
           leaf_id
    FROM data
    WHERE oid = '1'  -- start of subtree
    UNION ALL
    SELECT child.oid,
           child.value,
           parent.depth + 1,
           child.leaf_id
    FROM data AS child
    JOIN subtree AS parent ON child.parent = parent.oid
    ORDER BY depth DESC, leaf_id ASC
)
SELECT oid, value
FROM subtree
WHERE value IS NOT NULL

仅需按字典顺序对结果进行排序,才需要depthleaf_id值。

您应该在parent列上有一个索引。


至于字典下一个项目,首先考虑下面的CTE,它只是通过树,但记住下面最后一层的叶值:

WITH RECURSIVE parents(oid, parent, previous_leaf, step, leaf_id) AS (
    SELECT oid,
           parent,
           -1,
           0 AS step,
           leaf_id
    FROM data
    WHERE oid = '1.3.4'  -- start point
    UNION ALL
    SELECT parent.oid,
           parent.parent,
           child.leaf_id,
           child.step + 1,
           parent.leaf_id
    FROM data AS parent
    JOIN parents AS child ON parent.oid = child.parent
    ORDER BY step
)
SELECT oid, previous_leaf FROM parents

oid         previous_leaf
----------  -------------
1.3.4       -1             (1.)
1.3         4              (2.)
1           3              (3.)

对于每个结果行,我们搜索<{>} 下面的 oid值的子树时会发生什么情况,并且该子树中的顶级叶必须为的附加限制大于 previous_leaf值?

  1. 我们搜索1.3.4的孩子。 (previous_leaf无效。)
  2. 我们搜索1.3.4的更大的诽谤,例如1.3.5
  3. 我们搜索1.3的更大的诽谤,例如1.5
  4. 所以现在我们只需要进行子树搜索:

    WITH RECURSIVE parents(oid, parent, previous_leaf, step, leaf_id) AS (
        ... see above ...
    ),
    subtree(oid, value, depth, leaf_id, previous_leaf, step) AS (
        SELECT oid,
               NULL,            -- interesting items are only *below* top of subtree 
               0 AS depth,
               leaf_id,
               previous_leaf,
               step
        FROM parents
        UNION ALL
        SELECT child.oid,
               child.value,
               parent.depth + 1,
               child.leaf_id,
               -1,              -- previous_leaf mattered only at the top
               parent.step
        FROM data AS child
        JOIN subtree AS parent ON child.parent = parent.oid
        WHERE child.leaf_id > parent.previous_leaf
        ORDER BY step, depth DESC, leaf_id
    )
    SELECT oid, value
    FROM subtree
    WHERE value IS NOT NULL
    LIMIT 1                     -- only the first item