我正在研究一个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技术可用于实现此分层查询?即使百分比表可能包含同一个人的多个条目(百分比)?
答案 0 :(得分:2)
解决方案是利用以下链接中描述的功能进行层次化查询:
不是制作PATH
,而是想要计算乘法。
将其直接复制并粘贴到mysql控制台中。我在工作台上没有太多运气。此外,通过将hierarchy_sys_connect_by_path_percentage
和hierarchy_sys_connect_by_path_percentage_result
组合到一个存储过程中,可以进一步优化这一点。不幸的是,对于巨型数据集来说,这可能相当慢。
drop table people;
drop table percentages;
create table people
(
id int,
name varchar(10),
parent int
);
create table percentages
(
id int,
percentage float
);
insert into people values(1,' A ',0);
insert into people values(2,' B ',1);
insert into people values(3,' C ',2);
insert into people values(4,' D ',3);
insert into people values(5,' E ',1);
insert into people values(6,' F ',0);
insert into percentages values(1,0.70);
insert into percentages values(2,0.60);
insert into percentages values(3,0.10);
insert into percentages values(4,0.5);
insert into percentages values(5,0.40);
insert into percentages values(6,0.30);
DELIMITER $$
DROP FUNCTION IF EXISTS `hierarchy_sys_connect_by_path_percentage`$$
CREATE FUNCTION hierarchy_sys_connect_by_path_percentage(
delimiter TEXT,
node INT)
RETURNS TEXT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE _path TEXT;
DECLARE _id INT;
DECLARE _percentage FLOAT;
DECLARE EXIT HANDLER FOR NOT FOUND RETURN _path;
SET _id = COALESCE(node, @id);
SELECT Percentage
INTO _path
FROM percentages
WHERE id = _id;
LOOP
SELECT parent
INTO _id
FROM people
WHERE id = _id
AND COALESCE(id <> @start_with, TRUE);
SELECT Percentage
INTO _percentage
FROM percentages
WHERE id = _id;
SET _path = CONCAT( _percentage , delimiter, _path);
END LOOP;
END $$
DROP FUNCTION IF EXISTS `hierarchy_sys_connect_by_path_percentage_result`$$
CREATE FUNCTION hierarchy_sys_connect_by_path_percentage_result(
node INT)
RETURNS FLOAT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE _path TEXT;
DECLARE _id INT;
DECLARE _percentage FLOAT;
DECLARE EXIT HANDLER FOR NOT FOUND RETURN _path;
SET _id = COALESCE(node, @id);
SELECT Percentage
INTO _path
FROM percentages
WHERE id = _id;
LOOP
SELECT parent
INTO _id
FROM people
WHERE id = _id
AND COALESCE(id <> @start_with, TRUE);
SELECT Percentage
INTO _percentage
FROM percentages
WHERE id = _id;
SET _path = _percentage * _path;
END LOOP;
END $$
DELIMITER ;
SELECT hi.id AS ID,
p.Percentage,
hierarchy_sys_connect_by_path_percentage('*', hi.id) AS Calculation,
hierarchy_sys_connect_by_path_percentage_result(hi.id) AS Actual
FROM people hi
JOIN percentages p
ON hi.id = p.id;
<强>结果强>
+------+------------+-----------------+--------------------+
| ID | Percentage | Calculation | Actual |
+------+------------+-----------------+--------------------+
| 1 | 0.7 | 0.7 | 0.699999988079071 |
| 2 | 0.6 | 0.7*0.6 | 0.419999986886978 |
| 3 | 0.1 | 0.7*0.6*0.1 | 0.0419999994337559 |
| 4 | 0.5 | 0.7*0.6*0.1*0.5 | 0.0210000015795231 |
| 5 | 0.4 | 0.7*0.4 | 0.280000001192093 |
| 6 | 0.3 | 0.3 | 0.300000011920929 |
+------+------------+-----------------+--------------------+
格式化数字是微不足道的,所以我留给你... 更重要的是优化以减少对数据库的调用。
答案 1 :(得分:1)
步骤1 - 创建一个MySQL函数,以逗号分隔的TEXT列返回族树:
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
;
http://sqlfiddle.com/#!2/9da5b/12
的SQLFiddle结果:
+------+----------------+-----------------+----------------+
| 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 |
+------+----------------+-----------------+----------------+
答案 2 :(得分:0)
MySQL是 Relational DBS。您的要求需要Graph database。
但是,如果你留在MySQL,有一些方法可以添加一些图形功能。其中之一是Nested Sets的概念。但我不建议,因为它增加了很多复杂性。
答案 3 :(得分:0)
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
;
答案 4 :(得分:-1)
考虑切换到Postgres9,它支持recursive queries:
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
查看sqlFiddle演示