Search code examples
sqloracle-databaseplsqlcursorcycle

Grouping recursive cycle by common person


I am quite new to PL/SQL and I am struggling with quest that I get.

I have Legal and Natural entities. Each legal entity can have some natural entities and these natural entities can be tied to 1 or more entities.

  • Company A have disponent #1 and #2 and #3
  • Company B have disponent #2 and #4
  • Company C have disponent #4
  • Company D have disponent #5
  • Company E have disponent #6
  • Company F have disponent #6

The result should show which companies are tied through some disponent together. In real the relations are more "deep" and complicated. On my example the result should be something like this:

  • Company A - Group 1
  • Company B - Group 1
  • Company C - Group 1
  • Company D - Group 2
  • Company E - Group 3
  • Company F - Group 3

Example table in excel:

pic

I made 3 tables:

  • SNAPSHOT_FO - contains FO_CLUID (ID of natural entity)
  • SNAPSHOT_VAZBA - contains PO_cluid (ID of legal entity) and FO_cluid. This is the relations table.
  • ODLITE - contains FO_CLUID (ID of natural entity) and KOD (group ID) - empty at the beginning. Should be the final table which I can then join to PO_cluid and get the result.
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 ;
/

Then I will just join the table odlite ON FO_cluid to my PO_cluid table. But the code doesn't work at the moment... and I don't know any other way how to do it. If there is any easier way how to do it, please let me know :)

PS: I am not allowed to make temporary table in declare part because of restrictions in our company.

Thanks in advance!

ORACLE SQL DEVELOPER Version 19.2.1.247


Solution

  • 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;
    /
    

    This works :)