web-dev-qa-db-ja.com

MySQLのテキストフィールドから2つの連続した数字を抽出する方法は?

MySQLデータベースがあり、次のようなクエリがあります。

SELECT `id`, `originaltext` FROM `source` WHERE `originaltext` regexp '[0-9][0-9]'

これにより、2桁の数字を含むすべてのオリジナルテキストが検出されます。

これらの数値をフィールドとして返すにはMySQLが必要なので、さらに操作することができます。

理想的には、20を超える追加の基準を追加できるのは素晴らしいことですが、個別にそれを行うこともできます。

27
Steve

データベースでより多くの正規表現力が必要な場合は、 LIB_MYSQLUDF_PREG の使用を検討できます。これは、PCREライブラリをインポートするMySQLユーザー関数のオープンソースライブラリです。 LIB_MYSQLUDF_PREGは、ソースコード形式でのみ提供されます。これを使用するには、コンパイルしてMySQLサーバーにインストールできる必要があります。このライブラリをインストールしても、MySQLのビルトイン正規表現サポートは変更されません。次の追加機能を使用可能にするだけです。

PREG_CAPTUREは、文字列から正規表現の一致を抽出します。 PREG_POSITIONは、正規表現が文字列に一致する位置を返します。 PREG_REPLACEは、文字列の検索と置換を実行します。 PREG_RLIKEは、正規表現が文字列に一致するかどうかをテストします。

これらの関数はすべて、最初のパラメーターとして正規表現を取ります。この正規表現は、Perlの正規表現演算子のようにフォーマットする必要があります。例えば。正規表現がサブジェクトの大文字と小文字を区別せずに一致するかどうかをテストするには、MySQLコードPREG_RLIKE( '/ regex/i'、subject)を使用します。これは、PHPのpreg関数に似ています。これは、PHP文字列内の正規表現に//区切り文字を追加する必要があります。

よりシンプルなものが必要な場合は、この関数を変更して、ニーズに合わせて変更できます。

CREATE FUNCTION REGEXP_EXTRACT(string TEXT, exp TEXT)
-- Extract the first longest string that matches the regular expression
-- If the string is 'ABCD', check all strings and see what matches: 'ABCD', 'ABC', 'AB', 'A', 'BCD', 'BC', 'B', 'CD', 'C', 'D'
-- It's not smart enough to handle things like (A)|(BCD) correctly in that it will return the whole string, not just the matching token.

RETURNS TEXT
DETERMINISTIC
BEGIN
  DECLARE s INT DEFAULT 1;
  DECLARE e INT;
  DECLARE adjustStart TINYINT DEFAULT 1;
  DECLARE adjustEnd TINYINT DEFAULT 1;

  -- Because REGEXP matches anywhere in the string, and we only want the part that matches, adjust the expression to add '^' and '$'
  -- Of course, if those are already there, don't add them, but change the method of extraction accordingly.

  IF LEFT(exp, 1) = '^' THEN 
    SET adjustStart = 0;
  ELSE
    SET exp = CONCAT('^', exp);
  END IF;

  IF RIGHT(exp, 1) = '$' THEN
    SET adjustEnd = 0;
  ELSE
    SET exp = CONCAT(exp, '$');
  END IF;

  -- Loop through the string, moving the end pointer back towards the start pointer, then advance the start pointer and repeat
  -- Bail out of the loops early if the original expression started with '^' or ended with '$', since that means the pointers can't move
  WHILE (s <= LENGTH(string)) DO
    SET e = LENGTH(string);
    WHILE (e >= s) DO
      IF SUBSTRING(string, s, e) REGEXP exp THEN
        RETURN SUBSTRING(string, s, e);
      END IF;
      IF adjustEnd THEN
        SET e = e - 1;
      ELSE
        SET e = s - 1; -- ugh, such a hack to end it early
      END IF;
    END WHILE;
    IF adjustStart THEN
      SET s = s + 1;
    ELSE
      SET s = LENGTH(string) + 1; -- ugh, such a hack to end it early
    END IF;
  END WHILE;

  RETURN NULL;

END
12
Pentium10

MySQLには、正規表現を使用してテキストを抽出するための構文はありません。 REGEXPを使用して、2つの連続した数字を含む行を識別できますが、それらを抽出するには、この場合非常に難しい通常の文字列操作関数を使用する必要があります。

代替案:

  • データベースから値全体を選択し、クライアントで正規表現を使用します。
  • SQL標準をよりよくサポートしている別のデータベースを使用してください(オプションではないかもしれません)。次に、これを使用できます:SUBSTRING(originaltext from '%#[0-9]{2}#%' for '#')
9
Mark Byers

ストアドプロシージャ(関数)としてコードを使用しました。1つのブロック内の数字から構築された数値を抽出するために機能します。これは私の幅広いライブラリの一部です。

DELIMITER $$

--  2013.04 [email protected]
--  FindNumberInText("ab 234 95 cd", TRUE) => 234  
--  FindNumberInText("ab 234 95 cd", FALSE) => 95

DROP FUNCTION IF EXISTS FindNumberInText$$
CREATE FUNCTION FindNumberInText(_input VARCHAR(64), _fromLeft BOOLEAN) RETURNS VARCHAR(32)
BEGIN
  DECLARE _r              VARCHAR(32) DEFAULT '';
  DECLARE _i              INTEGER DEFAULT 1;
  DECLARE _start          INTEGER DEFAULT 0;
  DECLARE _IsCharNumeric  BOOLEAN;

  IF NOT _fromLeft THEN SET _input = REVERSE(_input); END IF;
  _loop: REPEAT
    SET _IsCharNumeric = LOCATE(MID(_input, _i, 1), "0123456789") > 0;
    IF _IsCharNumeric THEN
      IF _start = 0 THEN SET _start  = _i;  END IF;
    ELSE
      IF _start > 0 THEN LEAVE _loop;       END IF;
    END IF;
    SET _i = _i + 1;
  UNTIL _i > length(_input) END REPEAT;

  IF _start > 0 THEN
    SET _r = MID(_input, _start, _i - _start);
    IF NOT _fromLeft THEN SET _r = REVERSE(_r);  END IF;
  END IF;
  RETURN _r;
END$$
2
m227

私は同じ問題を抱えていますが、これは私が見つけた解決策です(ただし、すべての場合に機能しません):

  • LOCATE()を使用して、一致させたくない文字列の先頭と末尾を見つけます
  • MID()を使用して、その間の部分文字列を抽出します...
  • 正規表現は、確実に一致する行だけに一致するようにしてください。
2
Greg

文字列の一部を返したい場合:

_SELECT id , substring(columnName,(locate('partOfString',columnName)),10) from tableName;
_

Locate()は、Function Substring()の開始位置となる一致する文字列の開始位置を返します

0
U.Sharma

この質問が尋ねられてからかなり時間が経ったことは知っていますが、それを見つけて、それが私のカスタム正規表現の置き換えにとって良い挑戦になると思いました- このブログ投稿 を参照してください。

...そして良いニュースはそれができるということですが、それは何度も呼ばれる必要があります。 このオンラインrextesterデモ を参照してください。これは、以下のSQLに到達した仕組みを示しています。

SELECT reg_replace(
         reg_replace(
           reg_replace(
             reg_replace(
               reg_replace(
                 reg_replace(
                   reg_replace(txt,
                               '[^0-9]+',
                               ',',
                               TRUE,
                               1, -- Min match length
                               0 -- No max match length
                               ),
                             '([0-9]{3,}|,[0-9],)',
                             '',
                             TRUE,
                             1, -- Min match length
                             0 -- No max match length
                             ),
                           '^[0-9],',
                           '',
                           TRUE,
                           1, -- Min match length
                           0 -- No max match length
                           ),
                         ',[0-9]$',
                         '',
                         TRUE,
                         1, -- Min match length
                         0 -- No max match length
                         ),
                       ',{2,}',
                       ',',
                       TRUE,
                       1, -- Min match length
                       0 -- No max match length
                       ),
                     '^,',
                     '',
                     TRUE,
                     1, -- Min match length
                     0 -- No max match length
                     ),
                   ',$',
                   '',
                   TRUE,
                   1, -- Min match length
                   0 -- No max match length
                   ) AS `csv`
FROM tbl;
0
Steve Chambers

よりクリーンな方法は REGEXP_SUBSTR() を使用していると思います:

これにより、正確に2つの数字が抽出されます。

SELECT REGEXP_SUBSTR(`originalText`,'[0-9]{2}') AS `twoDigits` FROM `source`;

これは正確に2桁を抽出しますが、20-99から(例:1112 nullを返します。 1521 戻り値 52):

SELECT REGEXP_SUBSTR(`originalText`,'[2-9][0-9]') AS `twoDigits` FROM `source`;

私は両方ともv8.0でテストし、動作します。それだけです、幸運を!

0
ESL