Разница между двумя неупорядоченными строками с разделителями в PL / SQL - PullRequest
2 голосов
/ 08 сентября 2011

Существует ли простой элегантный метод для возврата разницы между двумя неупорядоченными разделенными строками в Oracle с использованием PL / SQL?

Пример:

String A: "a1, b4, g3, h6, t8, a0"
String B: "b4, h6, a0, t8, a1"

Разница: "g3"

У меня нет доступа к APEX_UTIL, как предлагается в этом ответе Разница между двумя неупорядоченными разделенными списками (Oracle)

Ответы [ 3 ]

1 голос
/ 08 сентября 2011

Не уверен насчет элегантности, но это сработает:

WITH t1 AS (SELECT 'a1, b4, g3, h6, t8, a0' str FROM dual),
     t2 AS (SELECT 'b4, h6, a0, t8, a1' str     FROM dual)
--
SELECT val
  FROM t1, 
       xmltable('/root/e/text()'
       passing xmltype('<root><e>' || 
                       replace(t1.str,', ','</e><e>') || 
                       '</e></root>')
       columns val varchar2(10) path '/')
MINUS
SELECT val
  FROM t2, 
       xmltable('/root/e/text()'
       passing xmltype('<root><e>' || 
                       replace(t2.str,', ','</e><e>') || 
                       '</e></root>')
       columns val varchar2(10) path '/')

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

0 голосов
/ 09 сентября 2011

Если вы знаете, что они будут разделены запятыми и / или пробелами, тогда это будет работать и будет намного проще.

create or replace function compare_strings ( PString1 char, Pstring2 char ) return char is 

   v_string1 varchar2(100) := replace(replace(Pstring1,',',''),' ','');
   v_string2 varchar2(100) := replace(replace(Pstring2,',',''),' ','');

begin

   if replace(translate( v_string1, v_string2, ' '), ' ', '') is null then
      return replace(translate( v_string2, v_string1, ' '), ' ', '') ;
   else
      return replace(translate( v_string1, v_string2, ' '), ' ', '');
   end if;

end;

РЕДАКТИРОВАТЬ: изменить на возвращаемую строку.

0 голосов
/ 08 сентября 2011

Я обычно предпочитаю использовать оператор MULTISET для подобных ситуаций.

Решение, приведенное ниже, не совсем элегантно, поскольку вам нужно токенизировать строку, чтобы использовать оператор MULTISET, но если вы получите списки в виде коллекций, это будет очень просто (или если у вас уже есть общий токенизатор). (Токенизатор ниже не очень быстрый.)

DECLARE
   TYPE VARCHARTABLE IS TABLE OF VARCHAR2(2000);

   A VARCHAR2(32767) := 'a1, b4, g3, h6, t8, a0';
   B VARCHAR2(32767) := 'b4, h6, a0, t8, a1';

   onlyInA VARCHARTABLE;
   onlyInB VARCHARTABLE;

   FUNCTION tokenize( v IN VARCHAR2 )
   RETURN VARCHARTABLE 
   IS
      mReturn VARCHARTABLE := VARCHARTABLE();
      mTemp   VARCHAR2(2000);
      mChar   VARCHAR2(1);
      mIdx    INTEGER := 1;

      PROCEDURE appendToken( token IN VARCHAR2 )
      IS
      BEGIN
         IF TRIM(token) IS NOT NULL THEN
            mReturn.EXTEND(1);
            mReturn( mReturn.LAST ) := TRIM(token);
         END IF;
      END appendToken;
   BEGIN
      LOOP
         mChar := SUBSTR( v, mIdx, 1);

         IF mChar = ',' THEN
               appendToken( mTemp );
               mTemp := NULL;
         ELSIF mChar IS NULL THEN 
            appendToken( mTemp );
            EXIT;
         ELSE
            mTemp := mTemp || mChar;
         END IF;

         mIdx := mIdx + 1;
      END LOOP;

      RETURN mReturn;
  END tokenize;

  FUNCTION toVarchar( v IN VARCHARTABLE )
  RETURN VARCHAR2
  IS
     mReturn VARCHAR2(32767);
     mIdx    INTEGER := 0;
  BEGIN
     mIdx := v.FIRST;

     WHILE mIdx IS NOT NULL LOOP
        IF mReturn IS NOT NULL THEN
           mReturn := mReturn || ',';
        END IF;
        mReturn := mReturn || v(mIdx);

        mIdx := v.NEXT(mIdx);
     END LOOP;

     RETURN mReturn;
  END toVarchar;
BEGIN
   onlyInA := tokenize(A) MULTISET EXCEPT tokenize(B);
   onlyInB := tokenize(B) MULTISET EXCEPT tokenize(A);

   DBMS_OUTPUT.put_line( 'Only in A : ' || toVarchar(onlyInA) );
   DBMS_OUTPUT.put_line( 'Only in B : ' || toVarchar(onlyInB) );
END;
...