Zbigniew ‘zibi’ Jarosik Ecie-pecie o wszechświecie
  • MySQL – funkcje do obróbki numerów ISBN

    Szef zlecił mi jakiś czas temu optymalizację przetwarzania numerów ISBN w naszym systemie. Dotychczas używana klasa napisana w PHP jest fajna, funkcjonalna, itp. Ma jedną dużą wadę. Jest napisana w PHP ;P Oznaczało to konieczność pobrania danych z MySQL do PHP, przetworzenia w PHP, zapisania do MySQL. W momencie, gdy danych do przetworzenia było _DUŻO_, cały proces trwał o wiele za długo. Jeszcze do tego cały proces miał się cyklicznie powtarzać co jakiś czas…

    Rozwiązanie – funkcje składowane obrabiające dane w samej bazie, bez konieczności wykonywania dużej ilości zapytań, bez przesyłania danych.

    Dzięki takiemu podejściu odśmiecenie dużej ilości numerów ISBN będących w najróżniejszych formatach (‘ISBN 123-456-78-9-0′, ‘numer isbn 1234567890′, ‘nr ISBN 1-2-3-4-5-67890′, itp, itd) zamyka się w jednym zapytaniu:

     
    UPDATE ksiazki SET isbn=ISBN13FromISBN10(isbn);

    Poniżej przykłady implementacji.

    Konwersja z ISBN10 na ISBN13:

     
    DROP FUNCTION IF EXISTS ISBN13FromISBN10;
    delimiter //
    CREATE FUNCTION ISBN13FromISBN10(isbnIN VARCHAR(255)) RETURNS VARCHAR(20)
    BEGIN
        DECLARE i,j INT;
        DECLARE isbnOUT VARCHAR(20);
        DECLARE POSITION INT;
        DECLARE checksum INT;
        DECLARE c CHAR(1);
        DECLARE done BOOLEAN;
    
        SET isbnOUT='';
        SET c='';
        SET i=1;
        SET POSITION=1;
        SET done=FALSE;
        SET checksum=0;
    
        REPEAT
            SET c=SUBSTR(isbnIN,i,1);
            IF c>='0' and c<='9' THEN
                SET checksum=checksum+c*POSITION;
                SET POSITION=position+1;
    
                SET isbnOUT=CONCAT(isbnOUT,c);
    
                IF position>9 THEN
                    SET checksum=checksum % 11;
                    IF checksum=10 THEN
                        SET checksum='X';
                    END IF;
    		SET i=i+1;
     
                    REPEAT
                        SET c=SUBSTR(isbnIN,i,1);
    
                        IF (c>='0' and c<='9') or c='X' THEN
                            SET done=TRUE;
                            IF c=checksum THEN
                                SET isbnOUT=CONCAT('978',isbnOUT);
                                SET done=TRUE;
     
                                SET j=1;
                                SET checksum=0;
    
                                REPEAT
                                    SET checksum=checksum+substr(isbnOUT,j,1);
                                    SET checksum=checksum+substr(isbnOUT,j+1,1)*3;
                                    SET j=j+2;
                                UNTIL j>12 END REPEAT;
     
                                SET checksum=(10-(checksum % 10)) % 10;
                                SET isbnOUT=CONCAT(isbnOUT,checksum);
                            ELSE
                                SET isbnOUT=NULL;
                            END IF;
                        END IF;
    
                        SET i=i+1;
                        IF i>LENGTH(isbnIN) and done=FALSE THEN
                            SET isbnOUT=NULL;
                            SET done=TRUE;
                        END IF;
                    UNTIL done END REPEAT;
                END IF;
            END IF;
    
            SET i=i+1;
            IF i>LENGTH(isbnIN) and done=FALSE THEN
                SET done=TRUE;
                SET isbnOUT=NULL;
            END IF;
        UNTIL done END REPEAT;
     
        RETURN isbnOUT;
    END;
    //
    delimiter ;
     
    SELECT ISBN13FromISBN10('83-7389-320-2');

    Normalizacja do ISBN13:

     
    DROP FUNCTION IF EXISTS ISBN13Normalise;
    delimiter //
    CREATE FUNCTION ISBN13Normalise(isbnIN VARCHAR(255)) RETURNS VARCHAR(20)
    BEGIN
        DECLARE isbnOUT VARCHAR(20);
        DECLARE POSITION INT;
        DECLARE len INT;
        DECLARE c CHAR(1);
    
        SET isbnOUT='';
        SET POSITION=1;
        SET len=0;
    
        REPEAT
            SET c=SUBSTR(isbnIN,POSITION,1);
            IF c>='0' and c<='9' THEN
                SET isbnOUT=CONCAT(isbnOUT,c);
                SET len=len+1;
            END IF;
            SET POSITION=position+1;
        UNTIL position>LENGTH(isbnIN) or len=13 END REPEAT;
     
        IF len<13 THEN
            SET isbnOUT=NULL;
        END IF;
     
        RETURN isbnOUT;
    END;
    //
    delimiter ;
     
    SELECT ISBN13Normalise('ISBN 978-837-389-320-7');

    Normalizacja do ISBN10:

     
    DROP FUNCTION IF EXISTS ISBN10Normalise;
    delimiter //
    CREATE FUNCTION ISBN10Normalise(isbnIN VARCHAR(255)) RETURNS VARCHAR(20)
    BEGIN
        DECLARE isbnOUT VARCHAR(20);
        DECLARE POSITION INT;
        DECLARE len INT;
        DECLARE c CHAR(1);
    
        SET isbnOUT='';
        SET POSITION=1;
        SET len=0;
    
        SET isbnIN=UPPER(isbnIN);
    
        REPEAT
            SET c=SUBSTR(isbnIN,POSITION,1);
            IF (c>='0' and c<='9') or c='X' THEN
                SET isbnOUT=CONCAT(isbnOUT,c);
                SET len=len+1;
            END IF;
            SET POSITION=position+1;
        UNTIL position>LENGTH(isbnIN) or len=10 or c='X' END REPEAT;
     
        IF len<10 THEN
            SET isbnOUT=NULL;
        END IF;
     
        RETURN isbnOUT;
    END;
    //
    delimiter ;
     
    SELECT ISBN10Normalise('ISBN 83-7389-320-2');

    Normalizacja zgrubna uwzględniająca ISBN10 i ISBN13 (może dawać niepewne wyniki):

     
    DROP FUNCTION IF EXISTS ISBNNormalize;
    delimiter //
    CREATE FUNCTION ISBNNormalize(isbnIN VARCHAR(255)) RETURNS VARCHAR(20)
    BEGIN
        DECLARE isbnOUT VARCHAR(20);
        DECLARE POSITION INT;
        DECLARE len INT;
        DECLARE c CHAR(1);
    
        SET isbnOUT='';
        SET POSITION=1;
        SET len=0;
    
        SET isbnIN=UPPER(isbnIN);
    
        REPEAT
            SET c=SUBSTR(isbnIN,POSITION,1);
            IF (c>='0' and c<='9') or c='X' THEN
                SET isbnOUT=CONCAT(isbnOUT,c);
                SET len=len+1;
            END IF;
            SET POSITION=position+1;
        UNTIL position>LENGTH(isbnIN) or c='X' END REPEAT;
     
        IF (len!=10 and len!=13)  THEN
            SET isbnOUT=NULL;
        END IF;
     
        RETURN isbnOUT;
    END;
    //
     
    delimiter ;
     
    SELECT ISBNNormalize('003-44-877_4-7');
    SELECT ISBN10CalcChecksum('0034487747');

    Wyliczenie sumy kontrolnej dla ISBN13

     
    DROP FUNCTION IF EXISTS ISBN13CalcChecksum;
    delimiter //
    CREATE FUNCTION ISBN13CalcChecksum(isbnIN VARCHAR(255)) RETURNS CHAR(1)
    BEGIN
        DECLARE POSITION INT;
        DECLARE checksum INT;
    
        SET POSITION=1;
        SET checksum=0;
    
        REPEAT
            SET checksum=checksum+substr(isbnIN,POSITION,1)+substr(isbnIN,POSITION+1,1)*3;
            SET POSITION=position+2;
        UNTIL position>12 END REPEAT;
     
        SET checksum=(10-(checksum % 10)) % 10;
     
        RETURN checksum;
    END;
    //
    delimiter ;
     
    SELECT ISBN13CalcChecksum('9788373893207');

    Wyliczenie sumy kontrolnej dla ISBN10

     
    DROP FUNCTION IF EXISTS ISBN10CalcChecksum;
    delimiter //
    CREATE FUNCTION ISBN10CalcChecksum(isbnIN VARCHAR(255)) RETURNS CHAR(1)
    BEGIN
        DECLARE POSITION INT;
        DECLARE checksum INT;
    
        SET POSITION=1;
        SET checksum=0;
    
        REPEAT
            SET checksum=checksum+SUBSTR(isbnIN,POSITION,1)*POSITION;
            SET POSITION=position+1;
        UNTIL position>9 END REPEAT;
     
        SET checksum=checksum % 11;
        IF checksum=10 THEN
            SET checksum='X';
        END IF;
     
        RETURN checksum;
    END;
    //
    delimiter ;
     
    SELECT ISBN10CalcChecksum('8373893202');
    No Comments