SQLite:使用WHERE子句自我加入

时间:2013-09-03 17:12:25

标签: sql sqlite where self-join

我有一个用于存储层次结构的表,它引用自身。我需要一个SQL语句来确定父节点的类型。下面我给出了我的表格结构以及样本数据,以便对我想要弄清楚的内容提供最佳解释。

NODES表

CREATE TABLE IF NOT EXISTS NODES(id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, parent_id INTEGER REFERENCES NODES(id) ON DELETE CASCADE

注意:parent_id可以为NULL以引用ROOT节点。

示例数据

INSERT INTO NODES(type, parent_id) VALUES('GRP', NULL); -- id: 1, title: Hello, World!
INSERT INTO NODES(type, parent_id) VALUES('TXT', 1);    -- id: 2, title: Print
INSERT INTO NODES(type, parent_id) VALUES('RND', 1);    -- id: 3, title: Random Output
INSERT INTO NODES(type, parent_id) VALUES('TXT', 3);    -- id: 4, title: OUTPUT #1
INSERT INTO NODES(type, parent_id) VALUES('TXT', 3);    -- id: 5, title: OUTPUT #2
INSERT INTO NODES(type, parent_id) VALUES('TXT', 3);    -- id: 6, title: OUTPUT #3

每个节点都有标题,我已在评论中列出,但为了实用性,我只是将它们放在评论中。我要做的是使用单个SQL语句来返回所有内容,但是输出#*使用父级属性。

我的尝试

SELECT id
FROM NODES
WHERE parent_id NOT IN (SELECT id
                        FROM NODES
                        WHERE type = 'RND');

我的尝试大部分都有效,但由于parent_id可以为NULL,因此我通过研究得知JOIN是一个更好的解决方案。我只是无法弄清楚如何以我想要的方式使SELF JOIN工作。

2 个答案:

答案 0 :(得分:1)

此查询基于parent_id将表连接到自身,并显示节点及其父节点中的所有字段。由于使用了左连接,因此结果将包括所有父节点,包括根节点。

由于同一个表在同一个查询中被引用了两次,因此必须使用别名来区分这两个表。语法“nodes as parent”创建别名“parent”。

SELECT nodes.*, parent.*
FROM nodes
LEFT JOIN nodes AS parent
ON nodes.parent_id = parent.id

要查找上述查询中父类型不等于“RND”的节点,您需要在查询中添加以下where子句。

WHERE parent.type != 'RND' OR parent.type IS NULL

我相信你在上面的查询中遗漏的重点是NULL值和比较运算符一起工作的方式。大多数比较运算符和任何其他值的NULL结果总是为false。这就是上面where子句中需要第二个条件的原因。 “IS”关键字是一个特殊的关键字,可用于检查空值。

答案 1 :(得分:0)

您的查询确实没有返回“Hello,World!”节点,因为与NULL的比较总是失败。 您可以通过添加以下特殊情况来修改此查询以包含没有父节点的节点:

SELECT id
FROM NODES
WHERE parent_id NOT IN (SELECT id
                        FROM NODES
                        WHERE type = 'RND')
   OR parent_id IS NULL

可以通过连接来执行此操作,但

  1. 您不能使用普通(内部)联接,因为这与NULL值不匹配,您必须使用外部联接;和
  2. 您仍然需要为无父节点添加特殊情况,因为外连接将缺失父节点的所有值都设置为NULL;和
  3. 您现在有两个表,因此您必须使用别名,并为每个列名指定表:
  4. SELECT child.id
    FROM nodes AS child
    LEFT JOIN nodes AS parent ON child.parent_id = parent.id
    WHERE parent.type <> 'RND'
       OR parent.type IS NULL