IT박스

MySQL 문자열에서 n 번째 단어를 추출하고 단어 발생을 계산하는 방법은 무엇입니까?

itboxs 2020. 12. 12. 10:16
반응형

MySQL 문자열에서 n 번째 단어를 추출하고 단어 발생을 계산하는 방법은 무엇입니까?


다음과 같은 mysql 쿼리를 갖고 싶습니다.

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

mysql의 모든 정규식 예제는 텍스트가 표현식과 일치하는지 쿼리하는 데 사용되지만 표현식에서 텍스트를 추출하지 않습니다. 그러한 구문이 있습니까?


다음은 OP의 특정 문제 (문자열의 두 번째 단어 추출 )에 대한 제안 된 솔루션 이지만 mc0e의 답변에서 언급했듯이 실제로 정규식 일치를 추출하는 것은 MySQL에서 기본적으로 지원되지 않습니다. 이것이 정말로 필요한 경우, 기본적으로 1) 클라이언트에서 사후 처리에서 수행하거나 2)이를 지원하기 위해 MySQL 확장을 설치하는 것입니다.


BenWells는 거의 정확합니다. 그의 코드에서 작업하면서 약간 조정 된 버전이 있습니다.

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

실례로 다음을 사용했습니다.

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

이것은 성공적으로 단어를 추출합니다. IS


문장에서 두 번째 단어를 추출하는 더 짧은 옵션 :

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

SUBSTRING_INDEX 용 MySQL 문서


http://dev.mysql.com/ 에 따르면 SUBSTRING 함수는 시작 위치를 사용하고 길이를 사용하므로 두 번째 단어의 함수는 다음과 같습니다.

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

아니요, 정규 표현식을 사용하여 텍스트를 추출하는 구문은 없습니다. 일반적인 문자열 조작 함수 를 사용해야합니다 .

또는 데이터베이스에서 전체 값 (또는 너무 많은 데이터 전송이 걱정되는 경우 처음 n 자)을 선택한 다음 클라이언트에서 정규식을 사용합니다.


다른 사람들이 말했듯이 mysql은 하위 문자열을 추출하는 정규식 도구를 제공하지 않습니다. 사용자 정의 함수를 사용하여 mysql을 확장 할 준비가 되었더라도 사용할 수 없다는 것은 아닙니다.

https://github.com/mysqludf/lib_mysqludf_preg

소프트웨어를 배포하려는 경우에는 그다지 도움이되지 않고 소프트웨어 설치에 장애가 될 수 있지만 사내 솔루션의 경우에는 적절할 수 있습니다.


Brendan Bullen의 답변을 JSON 문자열에서 특정 필드의 값을 검색하는 유사한 문제의 시작점으로 사용했습니다. 그러나 그의 답변에 대해 언급했듯이 완전히 정확하지는 않습니다. 왼쪽 경계가 원래 질문과 같은 공백이 아니라면 불일치가 증가합니다.

수정 된 솔루션 :

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

두 가지 차이점은 SUBSTRING 인덱스 매개 변수의 +1과 길이 매개 변수의 -1입니다.

"제공된 두 경계 사이에서 문자열의 첫 번째 발생을 찾는"보다 일반적인 솔루션 :

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

I don't think such a thing is possible. You can use SUBSTRING function to extract the part you want.


My home-grown regular expression replace function can be used for this.

Demo

See this DB-Fiddle demo, which returns the second word ("I") from a famous sonnet and the number of occurrences of it (1).

SQL

Assuming MySQL 8 or later is being used (to allow use of a Common Table Expression), the following will return the second word and the number of occurrences of it:

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 */

Explanation

A few tricks are used in the SQL above and some accreditation is needed. Firstly the regular expression replacer is used to replace all continuous blocks of non-word characters - each being replaced by a single tilda (~) character. Note: A different character could be chosen instead if there is any possibility of a tilda appearing in the text.

The technique from this answer is then used for transforming a string with delimited values into separate row values. It's combined with the clever technique from this answer for generating a table consisting of a sequence of incrementing numbers: 0 - 10,000 in this case.


The field's value is:

 "- DE-HEB 20% - DTopTen 1.2%"
SELECT ....
SUBSTRING_INDEX(SUBSTRING_INDEX(DesctosAplicados, 'DE-HEB ',  -1), '-', 1) DE-HEB ,
SUBSTRING_INDEX(SUBSTRING_INDEX(DesctosAplicados, 'DTopTen ',  -1), '-', 1) DTopTen ,

FROM TABLA 

Result is:

  DE-HEB       DTopTEn
    20%          1.2%

참고URL : https://stackoverflow.com/questions/4021507/how-to-extract-the-nth-word-and-count-word-occurrences-in-a-mysql-string

반응형