AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 280055
Accepted
jdids
jdids
Asked: 2020-11-21 09:36:59 +0800 CST2020-11-21 09:36:59 +0800 CST 2020-11-21 09:36:59 +0800 CST

父/子汇总汇总

  • 772

我正在努力解决这个问题。我相信有人会简化它。

我们有一个父/子关系,我需要根据销售数量和销售额来汇总。

我们有一个存储父母和他们的孩子的表。看起来像这样:

Parent    Component
1000      1300
1000      1301

下订单时,我们的用户订购零件编号 1000,然后添加行项目 1300、1301 作为选项。父零件没有分配给它们的销售价值,但组件确实具有价值。所以我要做的是将销售订单上的所有内容汇总到父级别(销售和数量)。

以下是销售订单的外观:

SalesOrder    StockCode    ParentChild    Qty    Price
0001          1000         P              1      0
0001          1300         C              1      500
0001          1301         C              1      350
0001          1301                        1      400

正如您在上面看到的,我们可以StockCode在没有父级的情况下在此处拥有组件(1301)(将这些视为替换零件)。如果销售订单行没有ParentChild值,则需要从汇总中排除。

这就是我希望这些数据的外观。父母/孩子应该只有父母的数量。

SalesOrder    StockCode    ParentChild    Qty    Price
0001          1000         P              1      850
0001          1301                        1      400

我猜 STUFF/FOR XML PATH 会是一个很好的起点吗?

这是测试数据:

CREATE TABLE #Structure (
  Parent INT
 ,Component INT )
 
 INSERT INTO #Structure (Parent, Component)
 SELECT 1000, 1300
 UNION 
 SELECT 1000, 1301
 
 
 CREATE TABLE #SalesOrder (
   SalesOrder VARCHAR(4)
  ,StockCode INT
  ,ParentChild CHAR(1)
  ,Qty INT
  ,Price INT )

 INSERT INTO #SalesOrder (SalesOrder, StockCode, ParentChild, Qty, Price)
 SELECT '0001', 1000, 'P', 1, 0
 UNION
 SELECT '0001', 1300, 'C', 1, 500
 UNION
 SELECT '0001', 1301, 'C', 1, 350
 UNION
 SELECT '0001', 1301, NULL, 1, 400
 
 SELECT * FROM #Structure

 SELECT * FROM #SalesOrder

 DROP TABLE #SalesOrder
 DROP TABLE #Structure
sql-server t-sql
  • 2 2 个回答
  • 491 Views

2 个回答

  • Voted
  1. Best Answer
    J.D.
    2020-11-21T11:05:45+08:002020-11-21T11:05:45+08:00

    TLDR:带有评论的最终查询在最后,但以下答案将通过构建它的步骤。

    因此,您可能想要的是递归 CTE(我不确定 STUFF 是否可以帮助您,至少不容易)。

    您应该有一个 Item 表,它是所有组件和组件项 ID 的不同列表,类似于以下示例:

    项目表

    如果我从您的上一个问题中没记错的话,您的 BOM 表应该具有与本示例类似的结构(我敢肯定还有其他字段):

    物料清单表

    递归 CTE 将帮助您将其展平,以便所有级别的所有子组件都与主要最终项目一起汇总(例如,组件 2003 与 2001 一起汇总到最终项目父项 1000 中)。

    这是递归 CTE 的示例:

    WITH CTE AS 
    (
        SELECT ItemId AS EndItemId, B.ParentItemId, B.ComponentItemId
        FROM #Item AS I
        INNER JOIN #BOM AS B
            ON I.ItemId = B.ParentItemId
    
        UNION ALL
    
        SELECT C.EndItemId, B.ParentItemId, B.ComponentItemId
        FROM CTE AS C
        INNER JOIN #BOM AS B
            ON C.ComponentItemId = B.ParentItemId
    )
    
    SELECT EndItemId, ParentItemId, ComponentItemId
    FROM CTE
    

    在我的示例数据集中,这会产生以下输出(每个组件的每个级别都回滚到原始最终项目):

    递归 CTE 输出

    注意第 16 行到第 18 行的最终项目“1000”。第 16 行和第 17 行是“级别 1”,第 18 行是“级别 2”,因为第 17 行上的组件“2004”下面有一个组件,因此它是行上的父项18 到组件“2005”。

    现在让我们引入 Component Price 列,以便稍后按 End Item 分组并总结总成本,如下所示:

    WITH CTE AS
    (
        SELECT ItemId AS EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice
        FROM #Item AS I
        INNER JOIN #BOM AS B
            ON I.ItemId = B.ParentItemId
    
        UNION ALL
    
        SELECT C.EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice
        FROM CTE AS C
        INNER JOIN #BOM AS B
            ON C.ComponentItemId = B.ParentItemId
    )
    
    SELECT EndItemId, ParentItemId, ComponentItemId, ComponentPrice
    FROM CTE
    

    添加了组件价格列

    最后,如果您在 End Item 列上添加 GROUP BY 并添加 SUM(ComponentPrice) 列,那么您可以获得 End Item 的总分解 BOM 价格,如下所示:

    WITH CTE AS
    (
        SELECT ItemId AS EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice
        FROM #Item AS I
        INNER JOIN #BOM AS B
            ON I.ItemId = B.ParentItemId
    
        UNION ALL
    
        SELECT C.EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice
        FROM CTE AS C
        INNER JOIN #BOM AS B
            ON C.ComponentItemId = B.ParentItemId
    )
    
    SELECT EndItemId, SUM(ComponentPrice) AS EndItemTotalPrice
    FROM CTE
    GROUP BY EndItemId
    

    最终产品的总价格:

    最终产品的总价格

    TLDR:

    您的最后一步是通过 ItemId 到 EndItemId 将这些汇总的最终项目价格加入到您的销售行表中,如下所示:

    WITH CTE AS -- This is our recursive CTE
    (
        SELECT I.ItemId AS EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice -- These are the fields you'll need for recursion and final grouping later on
        FROM #Item AS I -- Items is your starting point in the recursion (i.e. this defines your base case)
        INNER JOIN #BOM AS B -- Join to the BOMs table to get the child Components
            ON I.ItemId = B.ParentItemId -- Gets all BOM lines with this parent Item
    
        UNION ALL -- This is how we start to call the CTE recursively
    
        SELECT C.EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice -- Must select the same fields as before for the UNION ALL
        FROM CTE AS C -- This is our recursive case, a CTE referencing itself
        INNER JOIN #BOM AS B -- Joining to the BOM table again to get all child components but notice the difference in join clause
            ON C.ComponentItemId = B.ParentItemId -- This time the previous call's child becomes the parent (i.e. we're joining the BOM table back in where the child component item is the parent item in the BOM table)
    ),
    RolledUpCte AS -- This rolls up the results of the previous recursive CTE by our end assembly item
    (
        SELECT EndItemId, SUM(ComponentPrice) AS EndItemTotalPrice -- Summing up the price of all the components on the end item
        FROM CTE
        GROUP BY EndItemId -- Group by end item
    )
    
    SELECT SL.SalesLineId, SL.ItemId, RUC.EndItemTotalPrice -- Final select of the fields we need from our Sales Line table and our rolled up prices of our end items
    FROM RolledUpCte AS RUC
    INNER JOIN #SalesLine AS SL
        ON RUC.EndItemId = SL.ItemId
    

    销售线及其总的组件价格: 销售线及其夏季组件价格

    您的表架构显然与我的示例不同,但这是您需要做的概念和示例代码。而不是 ItemIds,它看起来像您有 StockCodes(或在您的 Item 和 BOM 表中调用的任何项目标识符)。

    唯一需要注意的其他重要事项是,如果您的 BOM 深度超过 2 级(例如,父级 -> 子级 -> 孙级 -> 曾孙级),那么您只想总结最低级别的组件项否则你最终会重复计算。例如,如果项目 A 具有组件 B,而组件 B 上具有组件 C 和 D,那么您只想将 A 的 C 和 D 的价格相加。如果将它们全部相加,那么您最终会通过添加 B 来重复计算+ C + D 为 A 的价格。在我过去的经验中,我们有一个名为 ItemType 的列,它标识了哪些项目是最低级别的组件项目,因此当我们进行汇总时,我们可以添加一个 WHERE ItemType = 'LowestLevelComponent' 但在您的情况下,所有具有一个孩子没有价格,所以你可能没有

    查找最低级别组件的总量也是相同的想法,但是您需要在递归 CTE 中为 ParentQuantity 设置一列,另一列将 ParentQuantity 乘以 Child 的数量(在查询的递归部分)像这样:

        SELECT ItemId AS EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice, B.ComponentQuantity, B.ComponentQuantity AS ComponentQuantityMultiplied
        FROM #Item AS I
        INNER JOIN #BOM AS B
            ON I.ItemId = B.ParentItemId
    
        UNION ALL
    
        SELECT C.EndItemId, B.ParentItemId, B.ComponentItemId, B.ComponentPrice, B.ComponentQuantity, (B.ComponentQuantity * C.ComponentQuantityMultiplied) AS ComponentQuantityMultiplied
        FROM CTE AS C
        INNER JOIN #BOM AS B
            ON C.ComponentItemId = B.ParentItemId
    

    如果您有任何问题,请随时告诉我,我会尽我所能解释。如果这感觉像是信息过载,也许只需从上面的 TLDR 最终完整查询开始,然后阅读评论以尝试了解它的作用。

    • 2
  2. eshirvana
    2020-11-21T10:32:39+08:002020-11-21T10:32:39+08:00

    这是使用 cte 和窗口函数的一个答案:

    ;WITH cte
    AS
        (
            SELECT
                P.SalesOrder
              , P.StockCode
              , P.ParentChild
              , P.Qty          
              , SUM(P.Price) OVER (PARTITION BY P.SalesOrder , REPLACE(P.ParentChild, 'C', 'P')) Price
            FROM
                #SalesOrder P
        )
    SELECT
        cte.SalesOrder
      , cte.StockCode
      , cte.ParentChild
      , cte.Qty
      , cte.Price
    
    FROM
        cte
    WHERE
        ISNULL(cte.ParentChild, 'P') = 'P';
    
    • 0

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve