英文:
Handle SQL recursion for multiple pallets and production runs
问题
我需要追踪生产批次的成本,并分摊到原始接收的托盘上。但是,一旦原始托盘经过生产批次,它们会与来自该生产批次的所有其他输入托盘混合在一起,然后创建多个输出托盘。理论上,这些输出托盘可以出售,也可以回到另一个生产批次,然后以完全不同的托盘再次出现,这个过程可以重复发生未知次数。表结构还增加了一层复杂性。所有托盘都在一个表中创建。
托盘表包括两种类型:
- N:最初收到的托盘
- P:从生产批次生产的托盘,并且ProdOrd字段填充了批次号
每当我尝试为这种情况建立CTE递归时,我都会遇到递归的无限循环,因为这是一对多关系。我认为SQL服务器在这个层次上无法处理它。我们有数千个原始托盘,然后它们在混洗中混合在一起。
我希望得到类似这样的结果:
托盘 | 成本类型 | 成本 | 类型 |
---|---|---|---|
1234 | 生产批次 | 3.00 | N |
1234 | 标签 | 0.25 | N |
1235 | 生产批次 | 3.00 | N |
1235 | 标签 | 0.25 | N |
成本是分摊到原始输入托盘的所有生产批次的总和。
英文:
I need to trace the costs of production runs and apportion back to the original received pallets. However, once the original pallets goes through a production run, they are mixed with all the other input pallets from that production run and they create multiple output pallets. Those output pallets in theory could be sold OR go back into another production run and come out again as completely different pallets and this process can repeat itself an unknown number of times. The table structure also adds a layer of complication. All of the pallets are created in one table.
Basic Hierarchal Structure
The Pallets table includes two types:
- N: The originally received Pallet
- P: Pallet produced from a Production run and ProdOrd field is populated with the Run Number
Every time I try to build out the CTE recursion for this scenario, I run into a indefinite loop of the recursion because it is a many to many relationship. I'm thinking that SQL server can't handle it at this level. We have thousands of original pallets and then they get mixed in the shuffle.
What I'm hoping for is something like this:
Pallet | CostType | Cost | Type |
---|---|---|---|
1234 | Run | 3.00 | N |
1234 | Labeling | 0.25 | N |
1235 | Run | 3.00 | N |
1235 | Labeling | 0.25 | N |
The cost is the Sum of all the production runs apportioned to the original input pallet.
答案1
得分: 0
以下是您要的翻译:
我认为我已经捕捉到递归CTE计算托盘内容在各种生产过程运行中传播的意图。
为了计算托盘内容在整个生产系列中的传播,我们首先必须计算每个步骤中从每个输入托盘到每个输出托盘的传播。该计算结果是输入托盘的数量、按批次的总输入、按批次的总输出和输出托盘的数量的复杂组合。
以下是我在几个步骤中提出的解决方案。
首先,计算每个生产运行和批次的输入和输出总数。我们将在以下步骤的计算中需要这些值。
接下来,计算与每个生产运行、输入和输出托盘组合相关的输入和输出。同一批次的多个输入托盘中剩余的材料被假定均匀混合并重新分配到输出托盘。
现在,我们可以使用递归CTE检索初始托盘信息,并针对每个托盘,跟随生产运行的路径,以计算原始托盘的内容如何在系列中传播。
有关演示,请参见此数据库示例。
这在某种程度上有点过于复杂,因为有一些中间计算实际上没有被使用。如果您有问题,请随时提问,但如果您愿意,我将把简化、增强或其他适应您需求的任务留给您。
将成本计算应用到附加步骤是后续练习。
英文:
I think I have captured the intent up to the point where the recursive CTE calculates the propagation of pallet contents through the various process runs.
In order to calculate the propagation of pallet content through the entire production series, we must first calculate the propagation from each input pallet to each output pallet at each step. That calculation turns out to be a non-trivial combination of quantity per input pallet, total input by lot, total output by lot, and quantity per output pallet.
The following is what I came up with across several steps.
First, calculate input and output totals for each production run and lot. We will need these values in the calculations of the following step
CREATE TABLE #InOutQty(
ProdOrd NVARCHAR(10), Lot NVARCHAR(12),
InTotalQty INT, OutTotalQty INT,
PassThroughFraction FLOAT, ConsumedFraction FLOAT
)
INSERT #InOutQty
SELECT IQ.ProdOrd, IQ.Lot, IQ.InTotalQty, ISNULL(OQ.OutTotalQty, 0),
A1.PassThroughFraction,
(1e0 - A1.PassThroughFraction) AS ConsumedFraction
FROM (
-- Total up inputs by production run and lot
SELECT I.ProdOrd, P.Lot, Sum(I.QTY) AS InTotalQty
FROM ProdInput I
JOIN Pallets P ON P.Pallet = I.Pallet
GROUP BY I.ProdOrd, P.Lot
) IQ
LEFT JOIN(
-- Total up inputs by production run and lot (might be none for certain inputs)
SELECT P.ProdOrd, P.Lot, Sum(P.OrgQTY) AS OutTotalQty
FROM Pallets P
WHERE P.Type = 'P'
GROUP BY P.ProdOrd, P.Lot
) OQ
ON OQ.ProdOrd = IQ.ProdOrd
AND OQ.Lot = IQ.Lot
CROSS APPLY (
-- Calculate how much is unused and repacked on a new pallet
SELECT (1e0 * ISNULL(OQ.OutTotalQty, 0) / IQ.InTotalQty) AS PassThroughFraction
) A1
Next calculate the input and output attributable to each combination of production run, input, and output pallets. Leftover materials from multiple input pallets of the same lot are assumed to be mixed evenly and reallocated to the output pallets.
CREATE TABLE #PalletInOutQty (
ProdOrd NVARCHAR(10), Lot NVARCHAR(12),
InputPallet NVARCHAR(12), OutputPallet NVARCHAR(12),
InputQty FLOAT, OutputQty FLOAT,
InputPalletFraction FLOAT, PassThroughFraction FLOAT
)
INSERT #PalletInOutQty
SELECT IOQ.ProdOrd, IOQ.Lot, IP.Pallet, OP.Pallet,
A1.InputQty,
(A1.InputQty * IOQ.PassThroughFraction) AS OutputQty,
(A1.InputQty * 1e0 / IP.OrgQty) AS InputPalletFraction,
IOQ.PassThroughFraction
FROM #InOutQty IOQ
JOIN ProdInput PI ON PI.ProdOrd = IOQ.ProdOrd
JOIN Pallets IP ON IP.Pallet = PI.Pallet AND IP.Lot = IOQ.Lot
JOIN Pallets OP ON OP.ProdOrd = IOQ.ProdOrd AND OP.Lot = IOQ.Lot
CROSS APPLY (
-- Input quantity for this in/out pallet combination is calculated as the
-- the selected input pallet qty times the fraction of the total output
-- included in the selected output pallet.
SELECT (PI.Qty * (OP.OrgQty * 1e0 / IOQ.OutTotalQty)) AS InputQty
) A1
Now we can use a recursive CTE to retrieve the initial pallet information, and for each pallet, follow the path through the production run to calculate how the content of that original pallet propagates through the series.
IF OBJECT_ID('tempdb..#PalletLineage') IS NOT NULL
DROP TABLE #PalletLineage
CREATE TABLE #PalletLineage (
Pallet NVARCHAR(12),
OriginPallet NVARCHAR(12),
Lineage NVARCHAR(1000),
Qty FLOAT,
OriginPalletFraction FLOAT
)
;WITH cte AS (
SELECT
P.Pallet,
P.Pallet AS OriginPallet,
CAST(P.Pallet AS NVARCHAR(MAX)) AS Lineage,
CAST(P.OrgQty AS Float) AS Qty,
1.0e0 AS OriginPalletFraction
FROM Pallets P
WHERE P.Type = 'N'
UNION ALL
SELECT
PIOQ.OutputPallet AS Pallet,
cte.OriginPallet AS OriginPallet,
CONCAT(cte.Lineage, ' > ', PIOQ.OutputPallet) AS Lineage,
(cte.Qty / P.OrgQty * PIOQ.OutputQty) AS Qty,
(cte.OriginPalletFraction * PIOQ.InputPalletFraction
* PIOQ.PassThroughFraction) AS OriginPalletFraction
FROM cte
JOIN #PalletInOutQty PIOQ ON PIOQ.InputPallet = cte.Pallet
JOIN pallets P ON P.Pallet = cte.Pallet
)
INSERT #PalletLineage
SELECT *
FROM cte
See this db<>fiddle for a demo.
This is somewhat over-engineered in that several intermediate calculations are not really used. Feel free to ask questions if you have them, but I'll leave it to you to strip it down, enhance it, or otherwise adapt to your needs if you wish.
Applying additional steps to apply cost calculations is left as a follow-up exercise.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论