繁体   English   中英

如何编写 SQL 查询来计算与其父组件一起销售的组件数量? (Postgres 11/递归 CTE?)

[英]How can I write a SQL query to calculate the quantity of components sold with their parent assemblies? (Postgres 11/recursive CTE?)

我的目标

计算作为其父组件的一部分出售的组件的总和。

我确信这一定是一个常见的用例,但我还没有找到导致我正在寻找的结果的文档。

背景

我在 CentOS 7 上运行 Postgres 11。我有一些如下表:

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 
);

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
);

还有一个像这样的视图,主要是将category key与relator.child_name关联起来进行过滤:

CREATE VIEW the_schema.relationships_with_child_catetgory AS (
    SELECT 
    r.parent_name,
    r.child_name, 
    r.child_quantity,
    n.thing_category AS child_category
    FROM 
    the_schema.relator r 
    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'), ('subChild1', 'component'), ('subChild2', 'component');

INSERT INTO the_schema.relator (parent_name, child_name, child_quantity)
VALUES ('parent1', 'child1', 1),('child1', 'subChild1', 10), ('child1', 'subChild2', 2);

INSERT INTO the_schema.sales (sold_name, sold_quantity)
VALUES ('parent1', 1), ('parent1', 2);

我需要构造一个查询,给定这些数据,将返回如下内容:

 child_name | sum_sold 
------------+----------
 subChild1  |       30
 subChild2  |        6
(2 rows)

问题是我没有第一个想法如何 go 关于这个,事实上当我打字时它变得越来越可怕。 我很难想象需要建立的联系,所以很难以合乎逻辑的方式开始。 通常,Molinaro 的SQL Cookbook有一些东西可以开始,它确实有一个关于分层查询的部分,但据我所知,它们都没有达到这个特定目的。

根据我在这个网站上的研究,我可能需要使用递归 CTE /公共表表达式,如这个问题/答案所示,但我在理解这种方法以及如何将它用于我的案子。

从上面链接的 E. Brandstetter 的答案中获取示例,我得出:

WITH RECURSIVE cte AS (
    SELECT 
    s.sold_name,
    r.child_name, 
    s.sold_quantity AS total 
    FROM 
    the_schema.sales s
    INNER JOIN 
    the_schema.relationships_with_child_catetgory r 
    ON s.sold_name = r.parent_name

    UNION  ALL

    SELECT 
    c.sold_name, 
    r.child_name, 
    (c.total  *  r.child_quantity)
    FROM 
    cte c 
    INNER JOIN 
    the_schema.relationships_with_child_catetgory r
    ON r.parent_name = c.child_name
) SELECT * FROM cte 

这就是其中的一部分:

 sold_name | child_name | total 
-----------+------------+-------
 parent1   | child1     |     1
 parent1   | child1     |     2
 parent1   | subChild1  |    10
 parent1   | subChild1  |    20
 parent1   | subChild2  |     2
 parent1   | subChild2  |     4
(6 rows)

但是,这些结果包括不需要的行(前两个),当我尝试通过将where r.child_category = 'component'添加到两个部分来过滤 CTE 时,查询不返回任何行:

 sold_name | child_name | total 
-----------+------------+-------
(0 rows)

当我尝试分组/聚合时,它给出以下错误: ERROR: aggregate functions are not allowed in a recursive query's recursive term

我被困在如何过滤掉不需要的行并发生聚合; 显然我无法理解这种递归 CTE 是如何工作的。 感谢所有指导!

基本上你有解决方案。 如果您也将数量和类别存储在 CTE 中,您可以在之后简单地添加WHERE过滤器和SUM聚合:

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

我的整个查询看起来像这样(仅在我上面提到的细节与您的不同):

演示:db<>小提琴

WITH RECURSIVE cte AS (
    SELECT 
        s.sold_name,
        s.sold_quantity,
        r.child_name,
        r.child_quantity,
        nc.thing_category as category
    FROM 
        sales s
    JOIN relator r
    ON s.sold_name = r.parent_name
    JOIN names_categories nc
    ON r.child_name = nc.thing_name
    
    UNION ALL
    
    SELECT
        cte.sold_name,
        cte.sold_quantity,
        r.child_name,
        r.child_quantity,
        nc.thing_category
    FROM cte
    JOIN relator r ON cte.child_name = r.parent_name
    JOIN names_categories nc
    ON r.child_name = nc.thing_name
)
SELECT
    child_name,
    SUM(sold_quantity * child_quantity)
FROM cte
WHERE category = 'component'
GROUP BY child_name

注意:我没有使用您的视图,因为我发现直接从表中获取数据而不是加入我已经拥有的数据更方便。 但这只是我个人喜欢的方式:)

好吧,我发现 CTE 可以用作子查询,它允许我需要的过滤和聚合:

SELECT
cte.child_name, 
sum(cte.total)
FROM
(
WITH RECURSIVE cte AS (
    SELECT 
    s.sold_name,
    r.child_name, 
    s.sold_quantity AS total 
    FROM 
    the_schema.sales s
    INNER JOIN 
    the_schema.relationships_with_child_catetgory r 
    ON s.sold_name = r.parent_name

    UNION  ALL

    SELECT 
    c.sold_name, 
    r.child_name, 
    (c.total  *  r.child_quantity)
    FROM 
    cte c 
    INNER JOIN 
    the_schema.relationships_with_child_catetgory r
    ON r.parent_name = c.child_name

) SELECT * FROM cte  ) AS cte
INNER JOIN 
the_schema.relationships_with_child_catetgory r1 
ON cte.child_name = r1.child_name 
WHERE r1.child_category = 'component'
GROUP BY cte.child_name
;

这给出了所需的行:

 child_name | sum 
------------+-----
 subChild2  |   6
 subChild1  |  30
(2 rows)

对于手头的实际情况,这很好,可能就足够了——但我怀疑 go 对此有更清晰的方法,所以我很想阅读所有其他提供的答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM