簡體   English   中英

如何提高MySQL中REGEXP字符串匹配的性能?

[英]How to improve performance for REGEXP string matching in MySQL?

前言:

我已經做了很多(重新)搜索,然后找到了以下SO post / answer: https : //stackoverflow.com/a/5361490/6095216 ,它與我要找的東西非常接近。 相同的代碼,但有一些更有用的注釋,出現在這里: http : //thenoyes.com/littlenoise/?p=136

問題描述:

我需要將MySQL TEXT數據的1列拆分為多列,其中原始數據具有以下格式(N <= 7):

{"field1":"value1","field2":"value2",...,"fieldN":"valueN"}

您可能會猜到,我只需要提取 ,然后將每個放到單獨的(預定義)列中即可。 問題在於,不能保證所有記錄的字段數量和順序都相同。 因此,使用SUBSTR / LOCATE等的解決方案不起作用,我需要使用正則表達式。 另一個限制是不能使用諸如LIB_MYSQLUDF_PREG之類的第三方庫(在上面我的第一個鏈接的答案中建議)。

到目前為止的解決方案/進度:

我已經修改了上述鏈接中的代碼,以使其返回從左到右的第一個/最短匹配; 否則,返回NULL。 我也對其進行了重構,使標識符對閱讀器/維護者更友好:)這是我的版本:

CREATE FUNCTION REGEXP_EXTRACT_SHORTEST(string TEXT, exp TEXT)
RETURNS TEXT DETERMINISTIC
BEGIN
    DECLARE adjustStart, adjustEnd BOOLEAN DEFAULT TRUE;
    DECLARE startInd INT DEFAULT 1;
    DECLARE endInd, strLen INT;
    DECLARE candidate TEXT;

    IF string NOT REGEXP exp THEN
        RETURN NULL;
    END IF;

    IF LEFT(exp, 1) = '^' THEN
        SET adjustStart = FALSE;
    ELSE
        SET exp = CONCAT('^', exp);
    END IF;
    IF RIGHT(exp, 1) = '$' THEN
        SET adjustEnd = FALSE;
    ELSE
        SET exp = CONCAT(exp, '$');
    END IF;

    SET strLen = LENGTH(string);
    StartIndLoop: WHILE (startInd <= strLen) DO
        IF adjustEnd THEN
            SET endInd = startInd;
        ELSE
            SET endInd = strLen;
        END IF;
        EndIndLoop: WHILE (endInd <= strLen) DO
            SET candidate = SUBSTRING(string FROM startInd FOR (endInd - startInd + 1));
            IF candidate REGEXP exp THEN
                RETURN candidate;
            END IF;
            IF adjustEnd THEN
                SET endInd = endInd + 1;
            ELSE
                LEAVE EndIndLoop;
            END IF;
        END WHILE EndIndLoop;
        IF adjustStart THEN
            SET startInd = startInd + 1;
        ELSE
            LEAVE StartIndLoop;
        END IF;
    END WHILE StartIndLoop;
    RETURN NULL;
END;

然后,我添加了一個輔助函數,以避免必須重復正則表達式模式,正如您從上面看到的那樣,所有字段都是相同的。 這是該函數(我放棄了使用回溯的嘗試-在MySQL中不受支持-作為注釋):

CREATE FUNCTION GET_MY_FLD_VAL(inputStr TEXT, fldName TEXT)
RETURNS TEXT DETERMINISTIC
BEGIN
    DECLARE valPattern TEXT DEFAULT '"[^"]+"'; /* MySQL doesn't support lookaround :( '(?<=^.{1})"[^"]+"'*/
    DECLARE fldNamePat TEXT DEFAULT CONCAT('"', fldName, '":');
    DECLARE discardLen INT UNSIGNED DEFAULT LENGTH(fldNamePat) + 2;
    DECLARE matchResult TEXT DEFAULT REGEXP_EXTRACT_SHORTEST(inputStr, CONCAT(fldNamePat, valPattern));
    RETURN SUBSTRING(matchResult FROM discardLen FOR LENGTH(matchResult) - discardLen);
END;

當前,我要嘗試做的就是使用上述代碼進行簡單的SELECT查詢。 它可以正常工作,但是。 IS。 SLOOOOOOOW ...最多只能拆分7個字段/列(並非所有記錄都具有全部7個)! 僅限於20條記錄,大約需要3分鍾-我總共大約有40,000條記錄(對於數據庫來說不是很多,對吧!!):)

因此,最后,我們得到了一個實際的問題:[如何]在性能上顯着改善上述算法/代碼(在這一點上幾乎是蠻橫的搜索),以便可以在實際的數據庫中運行該算法/代碼。合理的時間? 我開始研究已知的主要模式匹配算法,但很快就迷失了方向,難以確定此處合適的算法,這在很大程度上是由於可用選項的數量及其各自的限制,使用條件等所致。似乎在SQL中實現其中之一只是為了看看是否有幫助,可能需要大量工作。

注意:這是我有史以來的第一篇文章(!),所以如果有不清楚的地方,請(很好地)讓我知道,等等。 提前致謝。

正如上面的tadman和Matt Raines所建議的,我能夠通過解析JSON來解決這個問題。 作為JSON概念的新手,我只是根本不知道可以用這種方式完成它……有點尷尬,但經驗教訓!

無論如何,我在common_schema框架中使用了get_option函數: https : //code.google.com/archive/p/common-schema/ (可通過本文找到,它還演示了如何使用該函數: 在MySQL中解析JSON ) 。 結果,我的INSERT查詢運行大約需要15分鍾,而使用REGEXP解決方案則需要30多個小時。 謝謝,直到下次! :)

不要在SQL中執行; 請使用PHP或其他內置了用於解析JSON的工具的語言來完成此操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM