Sql 我怎样才能阻止我的产后复发性CTE无限循环?

Sql 我怎样才能阻止我的产后复发性CTE无限循环?,sql,postgresql,common-table-expression,recursive-query,Sql,Postgresql,Common Table Expression,Recursive Query,背景 我正在CentOS 7上运行Postgres 11。 多亏S-Man的回答,我最近在博士后学习了递归CTE的基础知识 问题 在处理一个密切相关的问题(计算捆绑包和组件中销售的零件)并使用此递归CTE时,我遇到了一个问题,查询无限期循环,从未完成 我追踪到relator表中存在非虚假的“自参考”条目,即父项名称和子项名称具有相同值的行 我知道这些是问题的根源,因为当我用测试表和数据重新创建情况时,不希望出现的循环行为发生在这些行存在时,而当这些行不存在或当UNION(不包括重复的返回行)时消

背景

我正在CentOS 7上运行Postgres 11。
多亏S-Man的回答,我最近在博士后学习了递归CTE的基础知识

问题

在处理一个密切相关的问题(计算捆绑包和组件中销售的零件)并使用此递归CTE时,我遇到了一个问题,查询无限期循环,从未完成

我追踪到
relator
表中存在非虚假的“自参考”条目,即
父项名称
子项名称
具有相同值的行

我知道这些是问题的根源,因为当我用测试表和数据重新创建情况时,不希望出现的循环行为发生在这些行存在时,而当这些行不存在或当
UNION
(不包括重复的返回行)时消失在CTE中使用,而不是
UNION ALL

我认为数据模型本身可能需要调整,这样就不需要这些“自引用”行,但现在,我需要做的是让这个查询在完成时返回所需的数据并停止循环

我怎样才能达到这个结果?非常感谢所有的指导

表格和测试数据

CREATE TABLE the_schema.names_categories (
    id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    created_at TIMESTAMPTZ DEFAULT now(),
    thing_name TEXT NOT NULL, 
    thing_category TEXT NOT NULL
);

CREATE TABLE the_schema.relator (
    id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    created_at TIMESTAMPTZ DEFAULT now(),
    parent_name TEXT NOT NULL, 
    child_name TEXT NOT NULL,
    child_quantity INTEGER NOT NULL 
);


/* NOTE: listing_name below is like an alias of a relator.parent_name as it appears in a catalog, 
required to know because it is these listing_names that are reflected by sales.sold_name */

CREATE TABLE the_schema.catalog_listings ( 
    id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    created_at TIMESTAMPTZ DEFAULT now(),
    listing_name TEXT NOT NULL, 
    parent_name TEXT NOT NULL
);

CREATE TABLE the_schema.sales (
    id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    created_at TIMESTAMPTZ DEFAULT now(),    
    sold_name TEXT NOT NULL,
    sold_quantity INTEGER NOT NULL
);

CREATE VIEW the_schema.relationships_with_child_category AS (
    SELECT 
    c.listing_name, 
    r.parent_name,
    r.child_name, 
    r.child_quantity,
    n.thing_category AS child_category
    FROM 
    the_schema.catalog_listings c
    INNER JOIN 
    the_schema.relator r 
    ON c.parent_name = r.parent_name
    INNER JOIN 
    the_schema.names_categories n 
    ON r.child_name = n.thing_name 
);

INSERT INTO the_schema.names_categories (thing_name, thing_category)
VALUES ('parent1', 'bundle'), ('child1', 'assembly'), ('child2', 'assembly'),('subChild1', 'component'), 
('subChild2', 'component'), ('subChild3', 'component');

INSERT INTO the_schema.catalog_listings (listing_name, parent_name)
VALUES ('listing1', 'parent1'), ('parent1', 'child1'), ('parent1','child2'), ('child1', 'child1'), ('child2', 'child2');

INSERT INTO the_schema.catalog_listings (listing_name, parent_name)
VALUES ('parent1', 'child1'), ('parent1','child2');


/* note the two 'self-referential' entries  */
INSERT INTO the_schema.relator (parent_name, child_name, child_quantity)
VALUES ('parent1', 'child1', 1),('child1', 'subChild1', 1), ('child1', 'subChild2', 1)
('parent1', 'child2', 1),('child2', 'subChild1', 1), ('child2', 'subChild3', 1), ('child1', 'child1', 1), ('child2', 'child2', 1);

INSERT INTO the_schema.sales (sold_name, sold_quantity)
VALUES ('parent1', 1), ('parent1', 2), ('listing1', 1);
当前查询无限期地循环所需的UNION ALL

WITH RECURSIVE cte AS (
    SELECT 
        s.sold_name,
        s.sold_quantity,
        r.child_name,
        r.child_quantity,
        r.child_category as category
    FROM 
        the_schema.sales s
    JOIN the_schema.relationships_with_child_category r
    ON s.sold_name = r.listing_name

    UNION ALL
    
    SELECT
        cte.sold_name,
        cte.sold_quantity,
        r.child_name,
        r.child_quantity,
        r.child_category
    FROM cte
    JOIN the_schema.relationships_with_child_category r 
    ON cte.child_name = r.parent_name

)
SELECT
    child_name,
    SUM(sold_quantity * child_quantity)
FROM cte
WHERE category = 'component'
GROUP BY child_name
;

只需使用
UNION
而不是
UNION ALL
就可以避免无限递归

描述实现:

  • 计算非递归项。对于
    UNION
    (但不是
    UNION ALL
    ),放弃重复的行。包括递归查询结果中的所有剩余行,并将它们放置在临时工作表中

  • 只要工作台不是空的,重复以下步骤:

  • 计算递归项,用工作表的当前内容替换递归自引用。对于
    UNION
    (但不是
    UNION ALL
    ),放弃重复的行和重复任何先前结果行的行。包括递归查询结果中的所有剩余行,并将它们放置在临时中间表中

  • 用中间表的内容替换工作表的内容,然后清空中间表


  • “消除重复项”应该会导致中间表在某个点为空,从而结束迭代。

    目录列表中表列表名称和父表名称对于child1child2relator表中,对于child1child2

    这些行正在创建循环递归

    只需从两个表中删除这两行:

    delete from catalog_listings where id in (4,5)
    delete from relator where id in (7,8)
    
    然后,您所需的输出将如下所示:

    孩子的名字 总和 子弟2 8. 子弟3 8. 子弟1 16
    请查看与子类别表的关系。这些数据有效吗?如何维持这种关系?在列表名称的第四列中,父项名称和子项名称具有相同的值(“child1”)。请查看表目录列表和关系抱歉,我没有仔细阅读您的问题。你已经提到了这个问题。现在,您是否只需要在不删除行的情况下获得此结果?这是正确的,非常感谢您的帮助!
    r.parent\u name r.child\u name
    做了这个把戏,循环停止了。非常欢迎您。最美好的祝福。