web-dev-qa-db-fra.com

Comment extraire deux chiffres consécutifs d'un champ de texte dans MySQL?

J'ai une base de données MySQL et j'ai une requête en tant que:

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

Ceci détecte tous les textes originaux qui ont des nombres avec 2 chiffres.

J'ai besoin de MySQL pour renvoyer ces nombres en tant que field , afin que je puisse les manipuler davantage.

Idéalement, si je peux ajouter des critères supplémentaires, cela devrait être> 20 serait formidable, mais je peux le faire séparément aussi.

27
Steve

Si vous voulez plus de puissance d'expression régulière dans votre base de données, vous pouvez envisager d'utiliser LIB_MYSQLUDF_PREG . Ceci est une bibliothèque open source de fonctions utilisateurs MySQL qui importe la bibliothèque PCRE. LIB_MYSQLUDF_PREG est fourni sous forme de code source uniquement. Pour l'utiliser, vous devez pouvoir le compiler et l'installer sur votre serveur MySQL. L'installation de cette bibliothèque ne modifie en aucun cas le support regex intégré de MySQL. Il ne fait que rendre disponibles les fonctions supplémentaires suivantes:

PREG_CAPTURE extrait une correspondance regex d'une chaîne. PREG_POSITION renvoie la position à laquelle une expression régulière correspond à une chaîne. PREG_REPLACE effectue une recherche et un remplacement sur une chaîne. PREG_RLIKE vérifie si une expression rationnelle correspond à une chaîne.

Toutes ces fonctions prennent une expression régulière comme premier paramètre. Cette expression régulière doit être formatée comme un opérateur d'expression régulière Perl. Par exemple. pour vérifier si regex correspond au sujet sans tenir compte de la casse, utilisez le code MySQL PREG_RLIKE ('/ regex/i', sujet). Ceci est similaire aux fonctions PHP de preg, qui nécessitent également les // délimiteurs supplémentaires pour les expressions régulières dans la chaîne PHP.

Si vous voulez quelque chose de plus simple, vous pouvez modifier cette fonction pour mieux répondre à vos besoins.

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 ne contient aucune syntaxe pour extraire du texte à l'aide d'expressions régulières. Vous pouvez utiliser REGEXP pour identifier les lignes contenant deux chiffres consécutifs, mais pour les extraire, vous devez utiliser les fonctions de manipulation de chaînes ordinaires, ce qui est très difficile dans ce cas.

Alternatives:

  • Sélectionnez la valeur entière de la base de données, puis utilisez une expression régulière sur le client.
  • Utilisez une base de données différente qui prend mieux en charge le standard SQL (peut ne pas être une option, je le sais). Ensuite, vous pouvez utiliser ceci: SUBSTRING(originaltext from '%#[0-9]{2}#%' for '#').
9
Mark Byers

J'ai utilisé mon code en tant que procédure stockée (Fonction), doit travailler pour extraire tout nombre construit à partir de chiffres dans un seul bloc. Ceci est une partie de ma bibliothèque plus large.

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

J'ai le même problème et voici la solution que j'ai trouvée (mais cela ne fonctionnera pas dans tous les cas):

  • utilisez LOCATE() pour trouver le début et la fin de la chaîne à laquelle vous ne voulez pas correspondre
  • utilisez MID() pour extraire la sous-chaîne entre ...
  • conservez l'expression rationnelle uniquement pour les lignes où vous êtes sûr de trouver une correspondance.
2
Greg

Si vous souhaitez renvoyer une partie d'une chaîne:

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

Locate() retournera la position de départ de la chaîne correspondante qui deviendra la position de départ de Function Substring()

0
U.Sharma

Je sais que cela fait un bon bout de temps depuis que cette question a été posée, mais je l’ai trouvée et j'ai pensé que ce serait un bon défi pour mon remplaçant de regex personnalisé - voir ce billet de blog .

... Et la bonne nouvelle est que c'est possible, même s'il doit être appelé plusieurs fois. Voir cette démo de rextester en ligne , qui montre le fonctionnement du SQL ci-dessous.

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