Как извлечь две последовательные цифры из текстового поля в MySQL?

У меня есть база данных MySQL, и у меня есть запрос как:

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

Это обнаруживает все оригинальные тексты, в которых есть номера с 2 цифрами.

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

В идеале, если бы я мог добавить дополнительные критерии, которые должны быть> 20, было бы здорово, но я могу сделать это и отдельно.

6 ответов

Если вам нужна большая мощность регулярных выражений в вашей базе данных, вы можете использовать LIB_MYSQLUDF_PREG. Это библиотека с открытым исходным кодом пользовательских функций MySQL, которая импортирует библиотеку PCRE. LIB_MYSQLUDF_PREG поставляется только в виде исходного кода. Чтобы использовать его, вам нужно будет скомпилировать его и установить на свой сервер MySQL. Установка этой библиотеки никоим образом не меняет встроенную поддержку регулярных выражений MySQL. Он просто делает доступными следующие дополнительные функции:

PREG_CAPTURE извлекает совпадение регулярного выражения из строки. PREG_POSITION возвращает позицию, в которой регулярное выражение соответствует строке. PREG_REPLACE выполняет поиск и замену строки. PREG_RLIKE проверяет, соответствует ли регулярное выражение строке.

Все эти функции принимают регулярное выражение в качестве первого параметра. Это регулярное выражение должно быть отформатировано как оператор регулярного выражения Perl. Например, чтобы проверить, соответствует ли регулярное выражение нечувствительному регистру, вы должны использовать код MySQL PREG_RLIKE('/regex/i', субъект). Это похоже на preg-функции PHP, которые также требуют дополнительных разделителей // для регулярных выражений внутри строки 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

В MySQL нет синтаксиса для извлечения текста с использованием регулярных выражений. Вы можете использовать REGEXP, чтобы идентифицировать строки, содержащие две последовательные цифры, но для их извлечения вы должны использовать обычные функции манипуляции со строками, что в этом случае очень сложно.

Альтернативы:

  • Выберите все значение из базы данных, затем используйте регулярное выражение на клиенте.
  • Используйте другую базу данных, которая лучше поддерживает стандарт SQL (я не знаю, как вариант). Тогда вы можете использовать это: SUBSTRING(originaltext from '%#[0-9]{2}#%' for '#'),

Я думаю, что более чистый способ использует REGEXP_SUBSTR():

Это извлекает ровно две любых цифры:

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

Это извлекает ровно две цифры, но от 20 до 99 (пример: 1112 вернуть ноль; 1521 возвращается 52):

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

Я тестирую оба в v8.0, и они работают. Вот и все, удачи!

У меня та же проблема, и вот решение, которое я нашел (но оно не будет работать во всех случаях):

  • использование LOCATE() чтобы найти начало и конец строки, которой вы не хотите соответствовать
  • использование MID() извлечь подстроку между...
  • сохраняйте регулярное выражение для соответствия только тем строкам, в которых вы обязательно найдете совпадение.

Я использовал свой код в качестве хранимой процедуры (функции), которая будет работать для извлечения любого числа, построенного из цифр в одном блоке. Это часть моей более широкой библиотеки.

DELIMITER $$

--  2013.04 michal@glebowski.pl
--  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$$

Если вы хотите вернуть часть строки:

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

Locate() вернет начальную позицию соответствующей строки, которая становится начальной позицией Function Substring()

Я знаю, что прошло довольно много времени с тех пор, как этот вопрос был задан, но натолкнулся на него и подумал, что это будет хорошим испытанием для моего пользовательского заменителя регулярных выражений - см. Этот пост в блоге.

... И хорошая новость в том, что это возможно, хотя его нужно вызывать довольно много раз. Посмотрите эту онлайн-демонстрацию 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;
Другие вопросы по тегам