单亲子表中层次数据的MySQL乘法
我正在从事一个项目,其MySQL数据库包含两个表;人员和百分比 人员表: +----+------+--------+ | ID | Name | Parent | +----+------+--------+ | 1 | A | 0 | | 2 | B | 1 | | 3 | C | 2 | | 4 | D | 3 | | 5 | E | 1 | | 6 | F | 0 | +----+------+--------+ 百分比表: +----+------------+ | ID | Percentage | +----+------------+ | 1 | 70% | | 2 | 60% | | 3 | 10% | | 4 | 5% | | 5 | 40% | | 6 | 30% | +----+------------+ 我正在查找的查询结果应如下所示: +----+------------+----------------+--------+ | ID | Percentage | Calculation | Actual | +----+------------+----------------+--------+ | 1 | 70 | 70% | 70.00% | | 2 | 60 | 70%*60% | 42.00% | | 3 | 10 | 70%*60%*10% | 4.20% | | 4 | 5 | 70%*60%*10%*5% | 0.21% | | 5 | 40 | 70%*40% | 28.00% | | 6 | 30 | 30% | 30.00% | +----+------------+----------------+--------+单亲子表中层次数据的MySQL乘法,mysql,hierarchical-data,Mysql,Hierarchical Data,我正在从事一个项目,其MySQL数据库包含两个表;人员和百分比 人员表: +----+------+--------+ | ID | Name | Parent | +----+------+--------+ | 1 | A | 0 | | 2 | B | 1 | | 3 | C | 2 | | 4 | D | 3 | | 5 | E | 1 | | 6 | F | 0 | +----+
“计算”列仅用于详细说明。有什么MySQL技术可以用来实现这种分层查询吗?即使百分比表可能包含多个条目,同一个人的百分比?解决方案是利用以下链接中描述的函数进行存档查询: 但是,您需要计算乘法,而不是创建路径 解决方案脚本 直接复制并粘贴到mysql控制台中。我在工作台上运气不太好。此外,这可以通过将hierarchy_sys_connect_by_path_percentage和hierarchy_sys_connect_by_path_percentage_结果组合到一个存储过程中来进一步优化。不幸的是,对于大型数据集来说,这可能相当缓慢 设置表和数据 结果 格式化数字很简单,所以我把它留给你。。。 更重要的是优化以减少对数据库的调用。MySQL是一种关系数据库。您的需求需要一个新的解决方案
但是,如果您继续使用MySQL,则有一些方法可以添加一些图形特性。其中一个是概念。但我不建议这样做,因为这会增加很多复杂性。步骤1-创建一个MySQL函数,以将族谱作为逗号分隔的文本列返回:
SELECT a.id
, ROUND(pa.percentage/100
* COALESCE(pb.percentage/100,1)
* COALESCE(pc.percentage/100,1)
* COALESCE(pd.percentage/100,1)
* 100,2) x
FROM people a
LEFT
JOIN people b
ON b.id = a.parent
LEFT
JOIN people c
ON c.id = b.parent
LEFT
JOIN people d
ON d.id = c.parent
LEFT
JOIN percentages pa
ON pa.id = a.id
LEFT
JOIN percentages pb
ON pb.id = b.id
LEFT
JOIN percentages pc
ON pc.id = c.id
LEFT
JOIN percentages pd
ON pd.id = d.id
;
DELIMITER //
CREATE FUNCTION fnFamilyTree ( id INT ) RETURNS TEXT
BEGIN
SET @tree = id;
SET @qid = id;
WHILE (@qid > 0) DO
SELECT IFNULL(p.parent,-1)
INTO @qid
FROM people p
WHERE p.id = @qid LIMIT 1;
IF ( @qid > 0 ) THEN
SET @tree = CONCAT(@tree,',',@qid);
END IF;
END WHILE;
RETURN @tree;
END
//
DELIMITER ;
然后使用以下SQL检索结果:
SELECT ppl.id
,ppl.percentage
,GROUP_CONCAT(pct.percentage SEPARATOR '*') as Calculations
,EXP(SUM(LOG(pct.percentage))) as Actual
FROM (SELECT p1.id
,p2.percentage
,fnFamilyTree( p1.id ) as FamilyTree
FROM people p1
JOIN percentages p2
ON p2.id = p1.id
) ppl
JOIN percentages pct
ON FIND_IN_SET( pct.id, ppl.FamilyTree ) > 0
GROUP BY ppl.id
,ppl.percentage
;
摆弄
结果:
+------+----------------+-----------------+----------------+
| ID | Percentage | Calculations | Actual |
+------+----------------+-----------------+----------------+
| 1 | 0.699999988079 | 0.7 | 0.699999988079 |
| 2 | 0.600000023842 | 0.7*0.6 | 0.420000009537 |
| 3 | 0.10000000149 | 0.7*0.6*0.1 | 0.04200000158 |
| 4 | 0.5 | 0.1*0.5*0.7*0.6 | 0.02100000079 |
| 5 | 0.40000000596 | 0.4*0.7 | 0.279999999404 |
| 6 | 0.300000011921 | 0.3 | 0.300000011921 |
+------+----------------+-----------------+----------------+
考虑切换到Postgres9,它支持:
查看demo您想要计算结果吗?MySQL对分层数据结构的支持非常糟糕。或者您需要编写一个存储过程。或者,您可以修改数据结构以包含根目录的整个路径,而不仅仅是父目录。@Gordon Linoff您完全抓住了问题的关键!或者尽可能频繁地将表连接到自身,或者在应用程序级别处理递归,或者切换到嵌套集模型。@Gordon这是因为它不必这样做。抱怨关系数据库上缺少图形功能与抱怨铺砌公路上的KTM是一样的。+1,因为这扩展了具有所需图形功能的表,并适度增加了复杂性。连接两个表将始终生成由多行错误组成的结果。有什么建议吗?它非常有效,但只有在百分比表上的ID列是唯一的情况下才有效。当行为作为外键值开始在多行上重复时,由多行组成的结果错误开始出现。为什么一个id有多个百分比?根据你的设计没有意义。不,在这种情况下没有意义。我正在用这个简单的案例模拟一个更复杂的场景。在实际场景中,每个人都有多个帐户,而这些帐户又有相应的比率。账户之间通过百分比进行交互。简言之,人们通过id与账户相关,账户通过账户id与RatioPercentage相关,账户id可以是贷方+或借方-。因此,计算应该通过人们的账户,并通过三个表中每个表的键进行。类似于将百分比视为对外关系。我知道这是一个头疼的问题。虽然知道Postgres可以处理这件事很好,但切换到其他数据库并不是一件小事,因此不是一个可行的解决方案。非常好!我特别喜欢通过对数加法进行乘法运算,这是我在数据库中从未考虑过的!!
DELIMITER //
CREATE FUNCTION fnFamilyTree ( id INT ) RETURNS TEXT
BEGIN
SET @tree = id;
SET @qid = id;
WHILE (@qid > 0) DO
SELECT IFNULL(p.parent,-1)
INTO @qid
FROM people p
WHERE p.id = @qid LIMIT 1;
IF ( @qid > 0 ) THEN
SET @tree = CONCAT(@tree,',',@qid);
END IF;
END WHILE;
RETURN @tree;
END
//
DELIMITER ;
SELECT ppl.id
,ppl.percentage
,GROUP_CONCAT(pct.percentage SEPARATOR '*') as Calculations
,EXP(SUM(LOG(pct.percentage))) as Actual
FROM (SELECT p1.id
,p2.percentage
,fnFamilyTree( p1.id ) as FamilyTree
FROM people p1
JOIN percentages p2
ON p2.id = p1.id
) ppl
JOIN percentages pct
ON FIND_IN_SET( pct.id, ppl.FamilyTree ) > 0
GROUP BY ppl.id
,ppl.percentage
;
+------+----------------+-----------------+----------------+
| ID | Percentage | Calculations | Actual |
+------+----------------+-----------------+----------------+
| 1 | 0.699999988079 | 0.7 | 0.699999988079 |
| 2 | 0.600000023842 | 0.7*0.6 | 0.420000009537 |
| 3 | 0.10000000149 | 0.7*0.6*0.1 | 0.04200000158 |
| 4 | 0.5 | 0.1*0.5*0.7*0.6 | 0.02100000079 |
| 5 | 0.40000000596 | 0.4*0.7 | 0.279999999404 |
| 6 | 0.300000011921 | 0.3 | 0.300000011921 |
+------+----------------+-----------------+----------------+
WITH RECURSIVE recp AS (
SELECT p.id, p.name, p.parent
, array[p.id] AS anti_loop
, array[pr.percentage ] AS percentages
, pr.percentage AS final_pr
FROM people p
JOIN percentages pr ON pr.id = p.id
WHERE parent = 0
UNION ALL
SELECT ptree.id, ptree.name, ptree.parent
, recp.anti_loop || ptree.id
, recp.percentages || pr.percentage
, recp.final_pr * pr.percentage
FROM people ptree
JOIN percentages pr ON pr.id = ptree.id
JOIN recp ON recp.id = ptree.parent AND ptree.id != ALL(recp.anti_loop)
)
SELECT id, name
, array_to_string(anti_loop, ' <- ') AS path
, array_to_string(percentages::numeric(10,2)[], ' * ') AS percentages_str
, final_pr
FROM recp
ORDER BY anti_loop