Oracle SQL Identify Siblings via siblings 并计算相对比例
Oracle SQ Identify Siblings via siblings and calculate related ratio
我有一组相关记录及其相关比率。不幸的是,并不是所有的比率都被捕获,而是它们可以从其他比率中导出。正如我可能需要 A -> D 的比率,但我没有直接的比率来参考,但我可以从下面的示例中通过 A->C (0.5) 的比率和 C-> 的比率得出它D (3) 因此 0.5 * 3 = 1.5。或者我可以通过反转 A->B (1/2 = 0.5) 的比率得出 B,然后取 A->C (0.5) 的比率 (1/2) * 0.5 = 0.25 等等.
例如
FROM_UNIT_ID TO_UNIT_ID RATIO
A B 2
A C 0.5
C D 3
我想要做的是计算所有可能路径及其比率的完整列表,如下所示。
FROM_UNIT_ID TO_UNIT_ID RATIO
A B 2
A C 0.5
C D 3
A D 1.5
B C 0.25
B D 0.75
B A 0.5
C A 2
C B 4
D A 0.666666667
D B 1.333333333
D C 0.333333333
使用以下 我已经能够推导出一个口粮的所有兄弟以获得可能路径的列表,但我无法调整此查询来计算比率。我正在使用 Oracle 11g。
SELECT
CONNECT_BY_ROOT( FROM_UNIT_ID ) AS FROM_UNIT_ID,
TO_UNIT_ID
FROM (
SELECT FROM_UNIT_ID, TO_UNIT_ID, RATIO FROM RATIOS
UNION
SELECT TO_UNIT_ID, UNIT_ID, 1/RATIO AS RATIO FROM RATIOS
)
WHERE CONNECT_BY_ROOT( FROM_UNIT_ID ) <> TO_UNIT_ID
CONNECT BY NOCYCLE
PRIOR TO_UNIT_ID = FROM_UNIT_ID
ORDER BY FROM_UNIT_ID, TO_UNIT_ID;
在 Oracle 11.2 及更高版本中(递归 WITH
子句可用),您可以这样做(假设输入 table 中的数据不自相矛盾):
with
test_data (from_unit_id, to_unit_id, ratio) as (
select 'A', 'B', 2 from dual union all
select 'A', 'C', 0.5 from dual union all
select 'C', 'D', 3 from dual
)
, symm_data (x, y, r) as (
select case when h.source = 1 then from_unit_id else to_unit_id end,
case when h.source = 1 then to_unit_id else from_unit_id end,
case when h.source = 1 then ratio else 1/ratio end
from test_data cross join
(select 1 as source from dual union all select 2 from dual) h
)
, all_nodes (x) as (
select distinct x
from symm_data
)
, rec (x, y, r) as (
select x, x, 1
from all_nodes
union all
select r.x, s.y, r.r * s.r
from rec r join symm_data s on r.y = s.x
)
cycle x, y set cycle to 'Y' default 'N'
select x as from_unit_id, y as to_unit_id, round(r, 4) as ratio
from rec
where cycle = 'N' and x != y
order by x, y
;
输出:
FROM_UNIT_ID TO_UNIT_ID RATIO
------------ ------------ ----------
A B 2
A C .5
A D 1.5
B A .5
B C .25
B D .75
C A 2
C B 4
C D 3
D A .6667
D B 1.3333
D C .3333
注意 - 我在最后的 SELECT
中使用了 ROUND
;通常它应该只在最终报告中使用,否则你不应该在任何地方四舍五入。
您可以通过计算所有个体的值来做到这一点 unit_ids 假设其中之一为 1,然后用它来计算比率:
--Creating the sample table
Create table ratios as
(select 'A' as FROM_UNIT_ID,'B' as TO_UNIT_ID,cast(2 as float) as RATIO from dual union
select 'A','C',0.5 from dual union
select 'C', 'D',3 from dual);
--Creating a list of unique unit_ids
Create table unit_id as
select a.*,CAST(NULL as float) as Val from
(select distinct from_unit_id as unit_id from ratios
UNION
select distinct to_unit_id as unit_id from ratios)a;
--Updating the first value to be 1
UPDATE unit_id
SET Val=1
WHERE rownum=1;
--Updating the other values based on the known values
UPDATE unit_id
SET Val=
(select Calc from
(Select a.Unit_ID,c.Val*b.ratio as Calc
FROM unit_id a
INNER JOIN ratios b on a.unit_id=b.to_unit_id
INNER JOIN unit_id c on c.unit_id=b.from_unit_id) b
WHERE Unit_ID.Unit_Id=b.Unit_Id)
WHERE Unit_ID.Val is null;
--Updating the other values based on the known values
--You can repeat this step a couple more times if needed
UPDATE unit_id
SET Val=
(select Calc from
(Select a.Unit_ID,c.Val*b.ratio as Calc
FROM unit_id a
INNER JOIN ratios b on a.unit_id=b.to_unit_id
INNER JOIN unit_id c on c.unit_id=b.from_unit_id) b
WHERE Unit_ID.Unit_Id=b.Unit_Id)
WHERE Unit_ID.Val is null;
select a.Unit_id as from_unit_id,
b.unit_id as to_unit_id,
trunc(a.val/b.val,2) as ratio
from unit_id a
cross join unit_id b
where a.unit_id<>b.unit_id
order by a.unit_id,b.unit_id
希望对您有所帮助。
我有一组相关记录及其相关比率。不幸的是,并不是所有的比率都被捕获,而是它们可以从其他比率中导出。正如我可能需要 A -> D 的比率,但我没有直接的比率来参考,但我可以从下面的示例中通过 A->C (0.5) 的比率和 C-> 的比率得出它D (3) 因此 0.5 * 3 = 1.5。或者我可以通过反转 A->B (1/2 = 0.5) 的比率得出 B,然后取 A->C (0.5) 的比率 (1/2) * 0.5 = 0.25 等等.
例如
FROM_UNIT_ID TO_UNIT_ID RATIO
A B 2
A C 0.5
C D 3
我想要做的是计算所有可能路径及其比率的完整列表,如下所示。
FROM_UNIT_ID TO_UNIT_ID RATIO
A B 2
A C 0.5
C D 3
A D 1.5
B C 0.25
B D 0.75
B A 0.5
C A 2
C B 4
D A 0.666666667
D B 1.333333333
D C 0.333333333
使用以下
SELECT
CONNECT_BY_ROOT( FROM_UNIT_ID ) AS FROM_UNIT_ID,
TO_UNIT_ID
FROM (
SELECT FROM_UNIT_ID, TO_UNIT_ID, RATIO FROM RATIOS
UNION
SELECT TO_UNIT_ID, UNIT_ID, 1/RATIO AS RATIO FROM RATIOS
)
WHERE CONNECT_BY_ROOT( FROM_UNIT_ID ) <> TO_UNIT_ID
CONNECT BY NOCYCLE
PRIOR TO_UNIT_ID = FROM_UNIT_ID
ORDER BY FROM_UNIT_ID, TO_UNIT_ID;
在 Oracle 11.2 及更高版本中(递归 WITH
子句可用),您可以这样做(假设输入 table 中的数据不自相矛盾):
with
test_data (from_unit_id, to_unit_id, ratio) as (
select 'A', 'B', 2 from dual union all
select 'A', 'C', 0.5 from dual union all
select 'C', 'D', 3 from dual
)
, symm_data (x, y, r) as (
select case when h.source = 1 then from_unit_id else to_unit_id end,
case when h.source = 1 then to_unit_id else from_unit_id end,
case when h.source = 1 then ratio else 1/ratio end
from test_data cross join
(select 1 as source from dual union all select 2 from dual) h
)
, all_nodes (x) as (
select distinct x
from symm_data
)
, rec (x, y, r) as (
select x, x, 1
from all_nodes
union all
select r.x, s.y, r.r * s.r
from rec r join symm_data s on r.y = s.x
)
cycle x, y set cycle to 'Y' default 'N'
select x as from_unit_id, y as to_unit_id, round(r, 4) as ratio
from rec
where cycle = 'N' and x != y
order by x, y
;
输出:
FROM_UNIT_ID TO_UNIT_ID RATIO
------------ ------------ ----------
A B 2
A C .5
A D 1.5
B A .5
B C .25
B D .75
C A 2
C B 4
C D 3
D A .6667
D B 1.3333
D C .3333
注意 - 我在最后的 SELECT
中使用了 ROUND
;通常它应该只在最终报告中使用,否则你不应该在任何地方四舍五入。
您可以通过计算所有个体的值来做到这一点 unit_ids 假设其中之一为 1,然后用它来计算比率:
--Creating the sample table
Create table ratios as
(select 'A' as FROM_UNIT_ID,'B' as TO_UNIT_ID,cast(2 as float) as RATIO from dual union
select 'A','C',0.5 from dual union
select 'C', 'D',3 from dual);
--Creating a list of unique unit_ids
Create table unit_id as
select a.*,CAST(NULL as float) as Val from
(select distinct from_unit_id as unit_id from ratios
UNION
select distinct to_unit_id as unit_id from ratios)a;
--Updating the first value to be 1
UPDATE unit_id
SET Val=1
WHERE rownum=1;
--Updating the other values based on the known values
UPDATE unit_id
SET Val=
(select Calc from
(Select a.Unit_ID,c.Val*b.ratio as Calc
FROM unit_id a
INNER JOIN ratios b on a.unit_id=b.to_unit_id
INNER JOIN unit_id c on c.unit_id=b.from_unit_id) b
WHERE Unit_ID.Unit_Id=b.Unit_Id)
WHERE Unit_ID.Val is null;
--Updating the other values based on the known values
--You can repeat this step a couple more times if needed
UPDATE unit_id
SET Val=
(select Calc from
(Select a.Unit_ID,c.Val*b.ratio as Calc
FROM unit_id a
INNER JOIN ratios b on a.unit_id=b.to_unit_id
INNER JOIN unit_id c on c.unit_id=b.from_unit_id) b
WHERE Unit_ID.Unit_Id=b.Unit_Id)
WHERE Unit_ID.Val is null;
select a.Unit_id as from_unit_id,
b.unit_id as to_unit_id,
trunc(a.val/b.val,2) as ratio
from unit_id a
cross join unit_id b
where a.unit_id<>b.unit_id
order by a.unit_id,b.unit_id
希望对您有所帮助。