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

希望对您有所帮助。