Sql server 在SQL中使用递归CTE获取每磅成本
我是一个相当不错的C#程序员,在T-SQL方面有很多经验,但是我很难用递归CTE来解决我的问题 我需要得到每磅食谱的成本 食谱由配料组成,但配料也可以是其他食谱。在配料表中,有一个名为associatedProductID的字段。如果它为空,那么我们只使用该成分的成本每磅(通过获取最新的IngreditStock成本每磅找到)。如果它不为空,那么该成分实际上是另一个配方,我们需要首先找到该另一配方的每磅成本。然后我们总结该成分的总成本(配方中使用的配料数量*该配料的成本/磅),然后最后除以配方的总磅数,得到配方的成本/磅数 下面是一些简化的表格信息Sql server 在SQL中使用递归CTE获取每磅成本,sql-server,recursion,common-table-expression,Sql Server,Recursion,Common Table Expression,我是一个相当不错的C#程序员,在T-SQL方面有很多经验,但是我很难用递归CTE来解决我的问题 我需要得到每磅食谱的成本 食谱由配料组成,但配料也可以是其他食谱。在配料表中,有一个名为associatedProductID的字段。如果它为空,那么我们只使用该成分的成本每磅(通过获取最新的IngreditStock成本每磅找到)。如果它不为空,那么该成分实际上是另一个配方,我们需要首先找到该另一配方的每磅成本。然后我们总结该成分的总成本(配方中使用的配料数量*该配料的成本/磅),然后最后除以配方的
- 表[配方]有一个intID
- 表[RecipeIngCredits]具有intRecipeID、intIngCreditId、十进制数量
- 表[成分]有intID,int关联产品ID
- 表[IngredientStock]具有intID、intIngredientID、十进制成本PERLB
[Recipes]
+------+
| ID |
+------+
| 11 |
+------+
| 465 |
+------+
| 356 |
+------+
| 1617 |
+------+
[Ingredients]
+--------------+--------------------+
| IngredientID | AssociatedPrductId |
+--------------+--------------------+
| 213 | NULL |
+--------------+--------------------+
| 214 | NULL |
+--------------+--------------------+
| 216 | NULL |
+--------------+--------------------+
| 218 | NULL |
+--------------+--------------------+
| 219 | 465 |
+--------------+--------------------+
| 225 | 356 |
+--------------+--------------------+
| 150 | NULL |
+--------------+--------------------+
| 213 | NULL |
+--------------+--------------------+
| 692 | 1617 |
+--------------+--------------------+
| 172 | NULL |
+--------------+--------------------+
| 218 | NULL |
+--------------+--------------------+
| 4 | NULL |
+--------------+--------------------+
| 691 | NULL |
+--------------+--------------------+
[RecipeIngredients]
+----------+--------------+-------------+
| RecipeID | IngredientID | Quanity |
+----------+--------------+-------------+
| 11 | 213 | 2 |
+----------+--------------+-------------+
| 11 | 214 | 1 |
+----------+--------------+-------------+
| 11 | 216 | 4.31494 |
+----------+--------------+-------------+
| 11 | 218 | 10.4125 |
+----------+--------------+-------------+
| 11 | 219 | 10.37085 |
+----------+--------------+-------------+
| 11 | 225 | 3.141971875 |
+----------+--------------+-------------+
| 465 | 150 | 0.0995 |
+----------+--------------+-------------+
| 465 | 213 | 0.25 |
+----------+--------------+-------------+
| 465 | 692 | 6.752298547 |
+----------+--------------+-------------+
| 356 | 172 | 200 |
+----------+--------------+-------------+
| 356 | 218 | 249.9 |
+----------+--------------+-------------+
| 1617 | 4 | 26.59274406 |
+----------+--------------+-------------+
| 1617 | 691 | 0.743192188 |
+----------+--------------+-------------+
在我看来,一个通用递归函数可能是这样的…(这显然不是CTE,也不起作用…)
提前谢谢
-David因为您可以在SQL中使用递归函数,所以可以使用类似这样的函数-如果关联的产品ID不为NULL,则调用查询以评估其成本-可能是将每磅成本除以/乘以CASE语句中的某个值 这是一个递归函数,与递归CTE相反
CREATE FUNCTION [dbo].[fn_getRecipeCostPerLbs]
(@ID INT)
RETURNS DECIMAL(24,12)
AS
BEGIN
DECLARE @COST DECIMAL(24,12) = 0;
SELECT @COST = SUM(CASE WHEN IngredientStock.AssociatedProductID IS NULL
THEN
IngredientStock.CostPerLb
ELSE
[dbo].[fn_getRecipeCostPerLbs](IngredientStock.AssociatedProductID)
END
)
FROM RecipeIngredints
join Ingredients
on RecipeIngredints.RecipeID = @ID
AND
Ingredients.id = RecipeIngredints.IngredientID
join IngredientStock
on
IngredientStock.ID = Ingredients.AssociatedProductID
RETURN @COST;
)
我猜你可能还需要找到每个配方的总成本和总重量,然后返回总成本/总重量-但这应该是可能的
编辑
也许像是-
您熟悉表之间的联接吗?我不确定表是如何设计为链接在一起的-您可能需要进行更改或提供更多信息-一个好方法是提供一个脚本来创建一小组示例表(尝试删除不必要的字段,因为它们太多了)并给出少量具有代表性的数据
SELECT @COST = SUM(RecipeIngredints.Quantity * (CASE WHEN Ingredients.AssociatedProductID IS NULL
THEN
IngredientStock.CostPerLb
ELSE
[dbo].[fn_getRecipeCostPerLbs](IngredientStock.AssociatedProductID)
END) )/ SUM(RecipeIngredints.Quantity)
FROM RecipeIngredints
join Ingredients
on RecipeIngredints.RecipeID = @ID
AND
Ingredients.id = RecipeIngredints.IngredientID
join IngredientStock
on
IngredientStock.ID = Ingredients.AssociatedProductID
因为您可以在SQL中使用递归函数,所以可以使用类似这样的函数—如果关联的产品ID不为NULL,则调用查询以计算其成本—可能是将每磅成本除以/乘以CASE语句中的某个值 这是一个递归函数,与递归CTE相反
CREATE FUNCTION [dbo].[fn_getRecipeCostPerLbs]
(@ID INT)
RETURNS DECIMAL(24,12)
AS
BEGIN
DECLARE @COST DECIMAL(24,12) = 0;
SELECT @COST = SUM(CASE WHEN IngredientStock.AssociatedProductID IS NULL
THEN
IngredientStock.CostPerLb
ELSE
[dbo].[fn_getRecipeCostPerLbs](IngredientStock.AssociatedProductID)
END
)
FROM RecipeIngredints
join Ingredients
on RecipeIngredints.RecipeID = @ID
AND
Ingredients.id = RecipeIngredints.IngredientID
join IngredientStock
on
IngredientStock.ID = Ingredients.AssociatedProductID
RETURN @COST;
)
我猜你可能还需要找到每个配方的总成本和总重量,然后返回总成本/总重量-但这应该是可能的
编辑
也许像是-
您熟悉表之间的联接吗?我不确定表是如何设计为链接在一起的-您可能需要进行更改或提供更多信息-一个好方法是提供一个脚本来创建一小组示例表(尝试删除不必要的字段,因为它们太多了)并给出少量具有代表性的数据
SELECT @COST = SUM(RecipeIngredints.Quantity * (CASE WHEN Ingredients.AssociatedProductID IS NULL
THEN
IngredientStock.CostPerLb
ELSE
[dbo].[fn_getRecipeCostPerLbs](IngredientStock.AssociatedProductID)
END) )/ SUM(RecipeIngredints.Quantity)
FROM RecipeIngredints
join Ingredients
on RecipeIngredints.RecipeID = @ID
AND
Ingredients.id = RecipeIngredints.IngredientID
join IngredientStock
on
IngredientStock.ID = Ingredients.AssociatedProductID
我自己解决了。如果有人想在这方面有所改进,请让我知道…谢谢你的帮助。而且这比我的C#/EF每个2秒的响应速度快8毫秒
CREATE FUNCTION [dbo].[fn_getRecipeCostPerPound]
(
@ID INT = 0
)
RETURNS DECIMAL(24,12)
AS
BEGIN
Declare @Ret decimal(24,12) = 0;
Declare @TotalLbs decimal(24,12) = [dbo].[fn_RecipeLbs](@ID)
DECLARE @MyCTETempTable TABLE (RecipeID int,
IngredientID int,
AssociatedProductID int,
Quantity decimal(24,12),
level int,
CostPerLbs decimal(24,12)
)
; with CTE as
(
select
ri.RecipeID
,ri.IngredientID
,i.AssociatedProductID
,dbo.fn_ConvertUomToPounds(ri.Quantity,ri.UnitOfMeasureID,i.specificgravity) as Quantity
,1 as level
from RecipeIngredients ri inner join Ingredients i on i.id = ri.IngredientID
where ri.RecipeID = @ID
union all
select
ri_child.RecipeID
,ri_child.IngredientID
,i_child.AssociatedProductID
,dbo.fn_ConvertUomToPounds(ri_child.Quantity,ri_child.UnitOfMeasureID,i_child.specificgravity) as Quantity
,level + 1
from RecipeIngredients ri_child inner join Ingredients i_child on i_child.id = ri_child.IngredientID --and i_child.AssociatedProductID is null
join CTE parent
on parent.AssociatedProductID = ri_child.RecipeID
)
INSERT INTO @MyCTETempTable (
RecipeID,
IngredientID,
AssociatedProductID,
Quantity,
level,
CostPerLbs
)
select
cte.*, isNull((select top 1 ist.CostPerLb from IngredientStock ist where ist.IngredientID = cte_i.id order by ist.id desc),0)
from CTE
inner join Ingredients cte_i on cte_i.id = CTE.IngredientID
SET @Ret = (select sum(Quantity * CostPerLbs) / @TotalLbs
from @MyCTETempTable tt
where RecipeID = @ID
group by RecipeID)
return @Ret
END
我自己解决了。如果有人想在这方面有所改进,请让我知道…谢谢你的帮助。而且这比我的C#/EF每个2秒的响应速度快8毫秒
CREATE FUNCTION [dbo].[fn_getRecipeCostPerPound]
(
@ID INT = 0
)
RETURNS DECIMAL(24,12)
AS
BEGIN
Declare @Ret decimal(24,12) = 0;
Declare @TotalLbs decimal(24,12) = [dbo].[fn_RecipeLbs](@ID)
DECLARE @MyCTETempTable TABLE (RecipeID int,
IngredientID int,
AssociatedProductID int,
Quantity decimal(24,12),
level int,
CostPerLbs decimal(24,12)
)
; with CTE as
(
select
ri.RecipeID
,ri.IngredientID
,i.AssociatedProductID
,dbo.fn_ConvertUomToPounds(ri.Quantity,ri.UnitOfMeasureID,i.specificgravity) as Quantity
,1 as level
from RecipeIngredients ri inner join Ingredients i on i.id = ri.IngredientID
where ri.RecipeID = @ID
union all
select
ri_child.RecipeID
,ri_child.IngredientID
,i_child.AssociatedProductID
,dbo.fn_ConvertUomToPounds(ri_child.Quantity,ri_child.UnitOfMeasureID,i_child.specificgravity) as Quantity
,level + 1
from RecipeIngredients ri_child inner join Ingredients i_child on i_child.id = ri_child.IngredientID --and i_child.AssociatedProductID is null
join CTE parent
on parent.AssociatedProductID = ri_child.RecipeID
)
INSERT INTO @MyCTETempTable (
RecipeID,
IngredientID,
AssociatedProductID,
Quantity,
level,
CostPerLbs
)
select
cte.*, isNull((select top 1 ist.CostPerLb from IngredientStock ist where ist.IngredientID = cte_i.id order by ist.id desc),0)
from CTE
inner join Ingredients cte_i on cte_i.id = CTE.IngredientID
SET @Ret = (select sum(Quantity * CostPerLbs) / @TotalLbs
from @MyCTETempTable tt
where RecipeID = @ID
group by RecipeID)
return @Ret
END
你描述你的餐桌的方式——似乎一份食谱是由一系列配料(配料)和这些配料的成本(我猜)包含在配料和IngredientStock中-你能把这4个表连接起来并总结数量吗*CostPerLbI意识到你说的“配料可以是一个配方”-但这在你的表中是如何满足的?有一些表和样本数据将大大改善这个问题。此外,这些标量函数可能会给你带来一些问题严重的性能问题。标量函数的性能非常差。您在此处调用它们的方式意味着它们每行运行一次。Andrew,在配料表中,有一个名为AssociatedProductID的字段。(如问题中所述)如果此值不为空,则该配料实际上是指另一个配方。与物料清单类似,物料清单将是一个产品列表……但可能会有另一个组件(物料清单)在物料清单上被引用为产品,谢谢您的评论。此查询旨在作为一个函数:[fn_getRecipeCostPerLbs](因此使其递归)…但这只是假设性的…并且fn_函数ToGetTotalRecipelbs只在求和后调用一次。我的意图是根本不使用上面的查询..而是获得帮助来构建CTE。正如您描述表的方式-似乎配方是从成分列表(RecipeIngCredits)中生成的,以及这些的成本(我猜)包含在配料和IngredientStock中-你能把这4个表连接起来并总结数量吗*CostPerLbI意识到你说的“配料可以是一个配方”-但这在你的表中是如何满足的?有一些表和样本数据将大大改善这个问题。此外,这些标量函数可能会给你带来一些问题严重的性能问题。标量函数的性能非常差。您在此处调用它们的方式意味着它们每行运行一次。Andrew,在配料表中,有一个名为AssociatedProductID的字段。(如问题中所述)如果该值不为空,那么该配料实际上是指另一个配方。很像物料清单是一个产品列表……但可能有另一个组件(物料清单)引用了一个配方