Группировка рекурсивного цикла по простому человеку - PullRequest
1 голос
/ 10 февраля 2020

Я довольно новичок в PL / SQL, и я борюсь с квестом, который я получаю.

У меня есть юридические и физические лица. Каждое юридическое лицо может иметь несколько физических лиц, и эти физические лица могут быть привязаны к 1 или более субъектам.

  • Компания А имеет диспонента № 1 и № 2 и № 3
  • Компания Б имеет диспетента № 2 и № 4
  • Компания C имеет диспетента № 4
  • Компания D имеет диспонента # 5
  • Компания E имеет диспонента # 6
  • Компания F имеет диспонента # 6

Результат должен показать, какие компании связаны через некоторое несогласие. В действительности отношения более «глубокие» и сложные. В моем примере результат должен выглядеть примерно так:

  • Компания A - Группа 1
  • Компания B - Группа 1
  • Компания C - Группа 1
  • Компания D - Группа 2
  • Компания E - Группа 3
  • Компания F - Группа 3

Пример таблицы в Excel:

pic

Я сделал 3 таблицы:

  • SNAPSHOT_FO - содержит FO_CLUID (идентификатор физического лица)
  • SNAPSHOT_VAZBA - содержит PO_cluid (идентификатор юридического лица) и FO_cluid. Это таблица отношений.
  • ODLITE - содержит FO_CLUID (идентификатор естественного объекта) и KOD (идентификатор группы) - пусто в начале. Должна быть последняя таблица, которую я затем могу присоединить к PO_cluid и получить результат.
CREATE GLOBAL TEMPORARY TABLE tbl_temp
(
   F_CL VARCHAR2(255) NULL,
   par NUMBER(5) NULL
)
ON COMMIT PRESERVE ROWS;

DECLARE
  party NUMBER := 1;
  r NUMBER := 1;

BEGIN
FOR CLUID IN (select FO_CLUID from snapshot_fo  WHERE ROWNUM =1)
LOOP
INSERT INTO tbl_temp (F_CL,par) VALUES (CLUID,party);

        WHILE  ( r> 0 )
        BEGIN

            MERGE INTO tbl_temp tg
            USING  ((SELECT distinct FO_CLUID from snapshot_vazba where PO_CLUID in (
                                                        SELECT distinct PO_CLUID 
                                                        from snapshot_vazba
                                                        where FO_CLUID in (select f_cl from tbl_temp where kod=party)
                                                    ))
            ) src
                on (tg.FO_cluid =src.FO_cluid)
            WHEN NOT MATCHED THEN
                INSERT (F_CL, par)
                VALUES (src.FO_CLUID, party)               
        SET r = SQL%ROWCOUNT
        END;
        INSERT INTO odlite (FO_CLUID, KOD) VALUES (tbl_temp.cluid,tbl_temp.party);
        delete from snapshot_fo where fo_cluid in (select fo_cluid from odlite);
        truncate table tbl_temp;
        party := party + 1;
        r := 1;
    END Loop;
end ;
/

Тогда я просто присоединю таблицу odlite ON FO_cluid к моей таблице PO_cluid. Но код не работает в данный момент ... и я не знаю другого способа, как это сделать. Если есть какой-то более простой способ, как это сделать, пожалуйста, дайте мне знать:)

PS: мне не разрешено делать временные таблицы в объявлении части из-за ограничений в нашей компании.

Спасибо заблаговременно!

ORACLE SQL РАЗРАБОТЧИК Версия 19.2.1.247

Ответы [ 3 ]

0 голосов
/ 11 февраля 2020
select po_cluid, dense_rank() over (order by min(connect_by_root(rn))) kod
  from (select rownum rn, s.* from snapshot_vazba s) s
  connect by nocycle prior fo_cluid = fo_cluid or prior po_cluid = po_cluid
  group by po_cluid

dbfiddle demo

Этот запрос возвращает желаемый результат, однако, боюсь, он слишком тяжел для вашей базы данных, если таблица большой. Ожидайте проблем с производительностью. Это циклы между компанией - диспонентом - компанией и находит минимальную общую ценность. Dense_rank() используется только для перечисления значений в последовательности.

0 голосов
/ 17 февраля 2020
declare
  v_cluid varchar2(30 char);
  v_party integer;
  change_num integer;

  function get_party return integer is
    grp integer;
  begin
    select
      nvl(max(kod),0)
    into
      grp
    from
      snapshot_vazba;

    return grp;
  end;

  function get_cluid return varchar2 is
    v_cluid varchar2(30 char);
  begin
    select
      nvl(max(fo_cluid), 'XNA')
    into
      v_cluid
    from
      (
        select
          fo_cluid,
          row_number() over (partition by 1 order by count_f desc) as rn
        from
          (
            select count(1) as count_f, fo_cluid from snapshot_vazba where kod is null group by fo_cluid
          )
      )
    where
      rn = 1;

    return v_cluid;
  end;
begin
  v_party := get_party + 1;
  v_cluid := get_cluid;

  while (v_cluid <> 'XNA' )
  loop
    update
      snapshot_vazba
    set
      kod = v_party
    where
      po_cluid in (select po_cluid from snapshot_vazba where fo_cluid = v_cluid) and kod is null;

    update
      snapshot_vazba
    set
      kod = v_party
    where
      kod is null and
      fo_cluid in (select fo_cluid from snapshot_vazba where kod = v_party);

    change_num := sql%rowcount;

    while (change_num > 0)
    loop
      update
        snapshot_vazba
      set
        kod = v_party
      where
        po_cluid in (select po_cluid from snapshot_vazba where kod = v_party) and kod is null;

      update
        snapshot_vazba
      set
        kod = v_party
      where
        kod is null and
        fo_cluid in (select fo_cluid from snapshot_vazba where kod = v_party);

      change_num := sql%rowcount;

    end loop;

    commit;

    v_party := v_party + 1;  
    v_cluid := get_cluid;
  end loop;
end;
/

Это работает :)

0 голосов
/ 10 февраля 2020

Я не совсем правильно понял задачу, но она вам, вероятно, нужна

select t1.po_cluid, t2.kod
  from snapshot_vazba t1
  join odlite t2 on t2.fo_cluid = t1.fo_cluid
 group by t1.po_cluid, t2.kod
 order by po_cluid, kod

Затем вы можете использовать слияние, вставить в select, для l oop или ввести коллекцию и обработать по желанию

...