我正在开发一种用于消费物价指数计算的软件,所有计算的第一步是计算产品类别中的产品权重。这应该在SQL Server端完成。我有MS SQL Server 2008。 产品目录存储在树中:
|id|ProductCode|ParentId|Weight|
--------------------------------
|1 |01 |NULL |0 |
|2 |01.1 | 1 |0 |
|3 |01.1.1 | 2 |0 |
|4 |01.1.1.101 | 3 |0.14 |
|5 |01.1.1.102 | 3 |0.1 |
|6 |01.1.1.103 | 3 |0.25 |
|7 |01.1.1.104 | 3 |0.02 |
|8 |01.1.2 | 2 |0 |
|9 |01.1.2.201 | 8 |0.05 |
|10|01.1.2.202 | 8 |0.3 |
--------------------------------
这是我表的简化结构,因此我需要计算类别的权重。
例如:
我的问题实际上是,我无法计算父类别的权重。
也许有人可以帮助我解决我的问题? 谢谢!
答案 0 :(得分:1)
这可以通过沿ParentId
链跟踪的递归方法来完成。在这种情况下,我将从叶节点开始(没有行使用此ID作为父ID),并在向上移动链时计算总和。
但是-由于存在路径 ProductCode
,使用这样的方法可能会更容易:
DECLARE @mockup TABLE(id INT, ProductCode VARCHAR(100),ParentId INT,[Weight] DECIMAL(10,4));
INSERT INTO @mockup VALUES
(1,'01' ,NULL,0 )
,(2,'01.1' ,1 ,0 )
,(3,'01.1.1' ,2 ,0 )
,(4,'01.1.1.101' ,3 ,0.14 )
,(5,'01.1.1.102' ,3 ,0.1 )
,(6,'01.1.1.103' ,3 ,0.25 )
,(7,'01.1.1.104' ,3 ,0.02 )
,(8,'01.1.2' ,2 ,0 )
,(9,'01.1.2.201' ,8 ,0.05 )
,(10,'01.1.2.202',8 ,0.3 );
DECLARE @depth INT=3;
SELECT groupCode
,*
FROM @mockup
CROSS APPLY(SELECT CAST('<x>' + REPLACE(ProductCode,'.','</x><x>') + '</x>' AS XML)
.query(N'for $frgmt in /x[position()<=sql:variable("@depth")]/text()
return <y>{concat(".",$frgmt)}</y>')
.value('substring(.,2,1000)','nvarchar(max)')) A(groupCode);
这使您可以定义一个 depth 并在那里进行下一步。
您可以创建一个cte,将其分组以获得不同的代码并加总
DECLARE @depth INT=3;
WITH cte As
(
SELECT *
FROM @mockup
CROSS APPLY(SELECT CAST('<x>' + REPLACE(ProductCode,'.','</x><x>') + '</x>' AS XML)
.query(N'for $frgmt in /x[position()<=sql:variable("@depth")]/text()
return <y>{concat(".",$frgmt)}</y>')
.value('substring(.,2,1000)','nvarchar(max)')) A(groupCode)
)
SELECT groupCode
,(SELECT SUM(cte2.[Weight]) FROM cte cte2 WHERE cte2.groupCode LIKE cte.groupCode + '%')
FROM cte
GROUP BY groupCode
结果
groupCode SumOfWeigth
01 0.8600
01.1 0.8600
01.1.1 0.5100
01.1.2 0.3500
答案 1 :(得分:1)
我认为Joakim的回答没有给出正确的结果。
也许这行得通:
-- Use a safe database
use tempdb
if object_id('Products') is not null drop table Products
create table Products (
id int,
ProductCode varchar(100),
ParentId int,
Weight float
)
-- Original sample data
insert into Products (id,ProductCode, ParentId, Weight)
SELECT 1 ,'01', NULL ,'0' UNION ALL
SELECT 2 ,'01.1' , '1' ,'0' UNION ALL
SELECT '3 ','01.1.1',' 2 ','0 ' UNION ALL
SELECT '4 ','01.1.1.101',' 3 ','0.14 ' UNION ALL
SELECT '5 ','01.1.1.102',' 3 ','0.1 ' UNION ALL
SELECT '6 ','01.1.1.103',' 3 ','0.25 ' UNION ALL
SELECT '7 ','01.1.1.104',' 3 ','0.02 ' UNION ALL
SELECT '8 ','01.1.2',' 2 ','0 ' UNION ALL
SELECT '9 ','01.1.2.201',' 8 ','0.05 ' UNION ALL
SELECT '10','01.1.2.202',' 8 ','0.3 '
-- Extra test data
insert into Products (id,ProductCode, ParentId, Weight)
SELECT 11 ,'01.2', 1 ,'1.1'
-- Calculate result
select b.ProductCode, SUM(a.Weight) as WeightSum
from Products a
join Products b on b.ProductCode = left(a.ProductCode, len(b.ProductCode)) and len(b.ProductCode) < len(a.ProductCode)
group by b.ProductCode
结果:
ProductCode WeightSum
01 1,96
01.1 0,86
01.1.1 0,51
01.1.2 0,35
答案 2 :(得分:1)
假设ProductCode
包含层次结构,则无需递归即可执行此操作。这是使用OUTER APPLY
的方法:
select p.*, (case when p.weight = 0 then pchild.weight else p.weight end) as weight
from products p outer apply
(select sum(pchild.weight) as weight
from products pchild
where p.weight = 0 and
pchild.productCode like p.productCode + '.%'
) pchild;
还有一个db<>fiddle。
您实际上可以将SELECT
简化为:
select p.*, coalesce(pchild.weight, p.weight) as weight