[PL/SQL] Porównanie dwóch zmiennych Varchar2


(Johnson Gs) #1

Witam. Mam dwa ciągi, przechowywane w zmiennej Varchar2, składające się z 49 znaków - 43 zera i 6 jedynek. Jeden z nich jest wzorcem. Moim zadaniem jest policzyć, ile razy na tej samej pozycji w obu ciągach występuje 1 (czyli odpowiednik logicznego AND), np.:

Wzór: 0010100000100000100000000000000100000000010000000

Ciąg: 0000100010000010000000000000000100001100000000000

Jak widać w tym przypadku jako wynik zostanie zwrócone: 2. Funkcja odpowiadająca za zliczanie:

CREATE OR REPLACE FUNCTION check_bet (bet_number NUMBER) RETURN NUMBER IS

    pattern VARCHAR2(50);

    other VARCHAR2(50);

    iterator NUMBER;

    counter NUMBER;

    pattern_char CHAR;

    other_char CHAR;

    CURSOR cur(id NUMBER) IS SELECT balls FROM results WHERE id_bet = id;

BEGIN

    iterator := 0;

    OPEN cur(iterator);

    FETCH cur INTO pattern;

    CLOSE cur;


    iterator := bet_number;

    OPEN cur(iterator);

    FETCH cur INTO other;

    CLOSE cur;


    counter := 0;

    FOR i IN 1..50 LOOP

        pattern_char := substr(pattern, i, 1);

        other_char := substr(other, i, 1);

        IF pattern_char = '1' AND other_char = '1' THEN

            counter := counter + 1;

        END IF;

    END LOOP;


    RETURN counter;  

END;

Funkcja działa i zwraca prawidłowe wyniki. Moje pytanie jest jednak takie: czy da się to rozwiązać "lepiej"? Chciałbym, żeby cała ta procedura działa szybciej, bo jak łatwo się domyślić w przypadku porównania 100 tys. takich ciągów trochę czasu to zajmie.


(litestep) #2

Generalnie nie, sprawdzasz każdy znak i nie da się tego przeskoczyć. Może skup się na zmniejszeniu ilości par, które porównujesz. W innych językach trzymałbyś BitSet/tablicę liczb, ANDował wszystko normalnymi operacjami arytmetycznymi a potem każdy bajt wyniku zamieniał na ilość jedynek na podstawie wcześniej wygenerowanej tabelki pomocniczej. Póki na bazie są to stringi, nie ma sensu kombinować bo ewentualna konwersja zajmie więcej czasu niż porównywanie, które robisz teraz.


(Wojciechmuszynski) #3

Samo porównywanie ciągów raczej dużo szybsze nie będzie.

Za to można sporo zaoszczędzić na odczycie wartości wzorca (rekord z id = 0) poza procedurą.

Zakładam, że sprawdzasz te 100 000 rekordów w pętli lub w zapytaniu.

W 1 przypadku warto wyliczyć "pattern" przed pętlą i przekazywać jako parametr funkcji "check_bet".

Zaoszczędzisz (100 tys - 1) otwarć i zamknięć kursora.

Jeżeli wywołujesz funkcję w zapytaniu, to sztucznie dodaj kolejną tabelę

" results as pattern " i także podaj pattern.balls jako parametr procedury.

Jeżeli przez przypadek zapytanie jest oparte na tabeli "results" - to wcale nie odczytuj wartości "balls" w procedurze, tylko zamiast ID przekaż wartość "balls".


(Johnson Gs) #4

Sprawa już zakończona. Faktycznie, nie dało rady bardziej tego przyśpieszyć, choć rozwiązanie powyżej trochę pomogło. Dziękuję wszystkim za pomoc :slight_smile: .