Значение Oracle Updating в одной коллекции с использованием другой коллекции - PullRequest
0 голосов
/ 07 марта 2019

У меня есть решение, которое работает с использованием циклов for, но этот метод неэффективен и работает плохо.Я новичок в использовании коллекций и пока не могу найти свой ответ в интернете.Может кто-то указать мне верное направление?Это упрощенная версия того, чего я пытаюсь достичь.

DECLARE
TYPE rec_numbers IS RECORD
(
  DIGIT NUMBER,
  ENGLISH VARCHAR2 (10),
  FRENCH VARCHAR2(10),
  ITALIAN VARCHAR2(10)
);
TYPE tab_numbers IS TABLE OF rec_numbers;
blk_num tab_numbers;
blk_num2 tab_numbers;

BEGIN

SELECT 1 DIGIT, 'ONE' ENGLISH, NULL FRENCH, NULL ITALIAN 
BULK COLLECT INTO blk_num
FROM DUAL;

SELECT 1 DIGIT, NULL ENGLISH, 'UN' FRENCH, 'UNO' ITALIAN 
BULK COLLECT INTO blk_num2
FROM DUAL;    

FOR i IN blk_num.FIRST .. blk_num.LAST LOOP
    FOR j IN blk_num2.FIRST .. blk_num2.LAST LOOP
        IF blk_num(i).digit = blk_num2(j).digit THEN
            blk_num(i).french := blk_num2(j).french;
            blk_num(i).italian := blk_num2(j).italian;
        END IF;
    END LOOP;      
END LOOP;

END;    

Ответы [ 3 ]

2 голосов
/ 07 марта 2019

Вы не упомянули, зачем вам здесь нужна коллекция.Если вы определили TYPE и коллекцию как объекты схемы, можно выполнить простой запрос соединения, используя функции TABLE, чтобы точно выполнить цикл for.

CREATE OR REPLACE
         TYPE rec_numbers AS OBJECT 
         ( digit      NUMBER,
         english    VARCHAR2(10),
         french     VARCHAR2(10),
         italian    VARCHAR2(10) );
         /

CREATE OR REPLACE    TYPE tab_numbers AS
              TABLE OF rec_numbers;
              /

Код

SET SERVEROUTPUT ON
DECLARE
     blk_num    tab_numbers;
     blk_num2   tab_numbers;
     blk_num3   tab_numbers;
BEGIN
     SELECT rec_numbers(1,'ONE',NULL,NULL) BULK COLLECT
       INTO blk_num
      FROM dual;

     SELECT rec_numbers(1,NULL,'UN','UNO') BULK COLLECT
       INTO blk_num2
     FROM dual;

SELECT rec_numbers
 ( a.digit,
   a.english, 
   COALESCE(b.french,a.french ),
   COALESCE(b.italian,a.italian)  
  ) BULK COLLECT
  INTO blk_num3
     FROM TABLE   ( blk_num  ) a
  LEFT JOIN TABLE ( blk_num2 ) b 
  ON a.digit = b.digit;
  blk_num := blk_num3;

  for i in 1..blk_num.count
  loop
  dbms_output.put_line(blk_num(i).digit ||','||blk_num(i).english
                ||','||blk_num(i).french||','||blk_num(i).italian
                );
  END LOOP;
END;
/

Выход

1,ONE,UN,UNO

PL/SQL procedure successfully completed.

Демонстрация

2 голосов
/ 07 марта 2019

Вложенные циклы могут быть медленными.Если blk_num и blk_num2 каждая имеют 1000 записей, вы выполняете 1000x1000 = 1 миллион итераций.Это производительность O (n ^ 2).

Вы можете уменьшить это значение до O (n), потратив время на преобразование blk_num2 в ассоциативный массив после его загрузки.Затем, вместо того, чтобы перебирать все записи в blk_num2, вы можете найти ту, которую ищете, просто запросив ее по значению индекса.

Вот пример того, что я имею в виду:

DECLARE
TYPE rec_numbers IS RECORD
(
  DIGIT NUMBER,
  ENGLISH VARCHAR2 (10),
  FRENCH VARCHAR2(10),
  ITALIAN VARCHAR2(10)
);
TYPE tab_numbers IS TABLE OF rec_numbers;
blk_num tab_numbers;
blk_num2 tab_numbers;
-- Define an associative array to copy blk_num2 into.
-- Note: I did not INDEX BY PLS_INTEGER because I do not know how big DIGIT can be.
-- if DIGIT will always fit into a PLS_INTEGER, you can use that instead.
TYPE assoc_numbers IS TABLE OF rec_numbers INDEX BY VARCHAR2(30);
blk_num2_aa assoc_numbers;


BEGIN

SELECT 1 DIGIT, 'ONE' ENGLISH, NULL FRENCH, NULL ITALIAN 
BULK COLLECT INTO blk_num
FROM DUAL;

SELECT 1 DIGIT, NULL ENGLISH, 'UN' FRENCH, 'UNO' ITALIAN 
BULK COLLECT INTO blk_num2
FROM DUAL;    

-- Copy blk_num2 into blk_num2_aa
FOR i in blk_num2.FIRST..blk_num2.LAST LOOP
  blk_num2_aa(to_char(blk_num2(i).digit)) := blk_num2(i);
END LOOP;

FOR i IN blk_num.FIRST .. blk_num.LAST LOOP
  -- Find the match based on the associative array's index value
  IF blk_num2_aa.exists(to_char(blk_num(i).digit)) THEN
    blk_num(i).french := blk_num2_aa(to_char(blk_num(i).digit)).french;
    blk_num(i).italian := blk_num2_aa(to_char(blk_num(i).digit)).italian;
  END IF;
END LOOP;

END;
1 голос
/ 07 марта 2019

Как насчет этого?

update (select * from table(blk_num )) a
set (FRENCH, ITALIAN) = 
    (select FRENCH, ITALIAN 
     from table(blk_num2) b 
     where a.DIGIT = b.DIGIT);
...