Oracle SQ Определить братьев и сестер через братьев и сестер и рассчитать соотношение - PullRequest
2 голосов
/ 03 марта 2020

У меня есть связанная группа связанных записей и их отношение. К сожалению, не все крысы ios захвачены, но скорее они могут быть получены от других крыс ios. Как и в случае, мне может потребоваться соотношение A -> D, но у меня нет прямого отношения, к которому можно обратиться, но я могу вывести его из приведенного ниже примера, взяв соотношение A -> C (0,5), а затем C -> D (3), поэтому 0,5 * 3 = 1,5. ИЛИ Я мог бы вывести В путем инвертирования отношения A-> B (1/2 = 0,5), а затем взять соотношение 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

Я хочу вычислить полный список всех возможных путей и их крыс ios, как показано ниже.

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

Используя следующий вопрос переполнения стека Я смог вывести всех братьев и сестер в рационе, чтобы получить список возможных путей, но я не смог адаптировать этот запрос для вычисления крысы ios. Я использую Oracle 11 г.

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;

Ответы [ 2 ]

2 голосов
/ 03 марта 2020

В Oracle 11.2 и выше (где доступно рекурсивное предложение WITH), вы можете сделать это следующим образом (при условии, что данные во входной таблице не противоречат сами себе):

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

Примечание - я использовал ROUND в финале SELECT; обычно это следует использовать только в итоговом отчете, иначе никуда не следует округлять числа.

0 голосов
/ 03 марта 2020

Вы можете сделать это, рассчитав значения для всех отдельных 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

Надеюсь, это поможет.

...