web-dev-qa-db-fra.com

Comment extraire le nième mot et compter les occurrences de mot dans une chaîne MySQL?

J'aimerais avoir une requête mysql comme celle-ci:

select <second Word in text> Word, count(*) from table group by Word;

Tous les exemples de regex dans mysql sont utilisés pour demander si le texte correspond à l'expression, mais pas pour extraire le texte d'une expression. Y a-t-il une telle syntaxe?

58
Noam

Ce qui suit est une solution proposée pour le problème spécifique du PO (extraire le deuxième mot d'une chaîne), mais il convient de noter que, comme l'indique la réponse de mc0e, l'extraction de correspondances de regex n'est pas prise en charge de manière autonome. boîte dans MySQL. Si vous en avez vraiment besoin, vous avez le choix entre 1) le faire en post-traitement sur le client ou 2) installer une extension MySQL pour le prendre en charge.


BenWells l'a très presque correct. À partir de son code, voici une version légèrement ajustée:

SUBSTRING(
  sentence,
  LOCATE(' ', sentence) + CHAR_LENGTH(' '),
  LOCATE(' ', sentence,
  ( LOCATE(' ', sentence) + 1 ) - ( LOCATE(' ', sentence) + CHAR_LENGTH(' ') )
)

Comme exemple de travail, j'ai utilisé:

SELECT SUBSTRING(
  sentence,
  LOCATE(' ', sentence) + CHAR_LENGTH(' '),
  LOCATE(' ', sentence,
  ( LOCATE(' ', sentence) + 1 ) - ( LOCATE(' ', sentence) + CHAR_LENGTH(' ') )
) as string
FROM (SELECT 'THIS IS A TEST' AS sentence) temp

Cela extrait avec succès le mot IS

44
Brendan Bullen

Option plus courte pour extraire le deuxième mot d'une phrase:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('THIS IS A TEST', ' ',  2), ' ', -1) as FoundText

Documents MySQL pour SUBSTRING_INDEX

27
Damien Goor

Selon http://dev.mysql.com/ , la fonction SUBSTRING utilise la position de départ, puis la longueur, si bien que la fonction du second mot serait: 

SUBSTRING(sentence,LOCATE(' ',sentence),(LOCATE(' ',LOCATE(' ',sentence))-LOCATE(' ',sentence)))
14
BenWells

Non, il n'y a pas de syntaxe pour extraire du texte à l'aide d'expressions régulières. Vous devez utiliser les fonctions de manipulation string .

Vous pouvez également sélectionner la valeur entière de la base de données (ou les n premiers caractères si vous craignez un transfert de données trop important), puis utilisez une expression régulière sur le client.

7
Mark Byers

J'ai utilisé la réponse de Brendan Bullen comme point de départ pour un problème similaire que j'ai eu, qui consistait à récupérer la valeur d'un champ spécifique dans une chaîne JSON. Cependant, comme je l'ai commenté sur sa réponse, ce n'est pas tout à fait exact. Si votre limite gauche n'est pas simplement un espace comme dans la question d'origine, la différence augmente.

Solution corrigée:

SUBSTRING(
    sentence,
    LOCATE(' ', sentence) + 1,
    LOCATE(' ', sentence, (LOCATE(' ', sentence) + 1)) - LOCATE(' ', sentence) - 1
)

Les deux différences sont le +1 dans le paramètre d'index SUBSTRING et le -1 dans le paramètre de longueur.

Pour une solution plus générale, "recherchez la première occurrence d'une chaîne entre deux limites fournies":

SUBSTRING(
    haystack,
    LOCATE('<leftBoundary>', haystack) + CHAR_LENGTH('<leftBoundary>'),
    LOCATE(
        '<rightBoundary>',
        haystack,
        LOCATE('<leftBoundary>', haystack) + CHAR_LENGTH('<leftBoundary>')
    )
    - (LOCATE('<leftBoundary>', haystack) + CHAR_LENGTH('<leftBoundary>'))
)
5
Hypolite Petovan

Comme d'autres l'ont déjà dit, mysql ne fournit pas d'outils regex pour extraire des sous-chaînes. Cela ne veut pas dire que vous ne pouvez pas les avoir si vous êtes prêt à étendre mysql avec des fonctions définies par l'utilisateur:

https://github.com/mysqludf/lib_mysqludf_preg

Cela ne vous aidera peut-être pas beaucoup si vous souhaitez distribuer votre logiciel, car cela l’empêchera de l’installer, mais une solution interne pourrait être appropriée.

5
mc0e

Je ne pense pas qu'une telle chose soit possible. Vous pouvez utiliser la fonction de sous-chaîne pour extraire la partie souhaitée.

2
user483085

Ma expression régulière maison-remplacée fonction peut être utilisé pour cela.

Démo

Voir cette démo de DB-Fiddle , qui renvoie le deuxième mot ("I") d’un sonnet célèbre et son nombre d’occurrences (1).

SQL

En supposant que MySQL version 8 ou ultérieure soit utilisé (pour permettre l’utilisation de Common Table Expression ), les éléments suivants renverront le deuxième mot et son nombre d’occurrences:

WITH cte AS (
     SELECT digits.idx,
            SUBSTRING_INDEX(SUBSTRING_INDEX(words, '~', digits.idx + 1), '~', -1) Word
     FROM
     (SELECT reg_replace(UPPER(txt),
                         '[^''’a-zA-Z-]+',
                         '~',
                         TRUE,
                         1,
                         0) AS words
      FROM tbl) delimited
     INNER JOIN
     (SELECT @row := @row + 1 as idx FROM 
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2, 
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3, 
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t4, 
      (SELECT @row := -1) t5) digits
     ON LENGTH(REPLACE(words, '~' , '')) <= LENGTH(words) - digits.idx)
SELECT c.Word,
       subq.occurrences
FROM cte c
LEFT JOIN (
  SELECT Word,
         COUNT(*) AS occurrences
  FROM cte
  GROUP BY Word
) subq
ON c.Word = subq.Word
WHERE idx = 1; /* idx is zero-based so 1 here gets the second Word */

Explication

Le SQL ci-dessus utilise quelques astuces et une accréditation est nécessaire. Premièrement, le substitut d'expression régulière est utilisé pour remplacer tous les blocs continus de caractères non-Word, chacun étant remplacé par un seul caractère tilda (~). Remarque: un caractère différent peut être choisi à la place s'il est possible qu'un tilda apparaisse dans le texte.

La technique de cette réponse est ensuite utilisée pour transformer une chaîne avec des valeurs délimitées en valeurs de ligne séparées. Il est combiné à la technique intelligente de cette réponse pour générer un tableau constitué d'une séquence de nombres incrémentiels: 0 à 10 000 dans ce cas.

0
Steve Chambers