Contar o número de ocorrências de uma sequência em um campo VARCHAR?


175

Eu tenho uma tabela como esta:

TITLE          |   DESCRIPTION
------------------------------------------------
test1          |   value blah blah value
test2          |   value test
test3          |   test test test
test4          |   valuevaluevaluevaluevalue

Eu estou tentando descobrir como retornar o número de vezes que uma seqüência ocorre em cada uma das descrições.

Portanto, se eu quiser contar o número de vezes que o 'valor' aparece, a instrução sql retornará isso:

TITLE          |   DESCRIPTION                  |   COUNT
------------------------------------------------------------
test1          |   value blah blah value        |   2
test2          |   value test                   |   1
test3          |   test test test               |   0
test4          |   valuevaluevaluevaluevalue    |   5

Há alguma maneira de fazer isso? Eu não quero usar php, apenas mysql.


4
As respostas abaixo o levarão até lá. No entanto, não se esqueça de usar em CHAR_LENGTH()vez de LENGTH()usar caracteres multibyte.
inhan 10/09/12

Este tópico também foi respondido aqui
Delickate

Oi, como faço isso com a consulta sqlserver?
aintno12u

COMPRIMENTO ([campo]) - COMPRIMENTO (SUBSTITUIR ([campo], '[char_to_find]', '')))
Phoenix

Respostas:


343

Isso deve fazer o truque:

SELECT 
    title,
    description,    
    ROUND (   
        (
            LENGTH(description)
            - LENGTH( REPLACE ( description, "value", "") ) 
        ) / LENGTH("value")        
    ) AS count    
FROM <table> 

55
Esta solução é incrível, exatamente o que eu precisava! Mas observe que LENGTH () não é seguro para vários bytes e você pode ter erros estranhos. Use CHAR_LENGTH () em vez disso :)
nico gawenda

1
não há diferença no uso de LENGTH()e CHAR_LENGTH()enquanto dividido no mesmo byte / char de contagem. @nicogawenda
MohaMad 8/17

3
@chyupa undevaluetem valuenele, por isso deve ser contado. Se você deseja apenas contar palavras completas, talvez seja necessário procurar por "valor" ou melhorar algo mais complicado, como usar regex.
PhoneixS

2
Observe que você encontra contagens erradas ao pesquisar por texto que também possui palavras com letras maiúsculas (como o alemão, onde todos os substantivos são escritos com letras maiúsculas). O SUBSTITUIR substitui apenas correspondências exatas. Para considerar todas as palavras, você precisa alterar a substituição acima para: LENGTH( REPLACE ( LOWER(description), "value", "") )e verifique se "value" está sempre em minúsculas usando PHP strtolower(). PS: Essa solução acima me ajudou a criar meu próprio mecanismo de pesquisa e a ponderar os resultados pelo número de palavras no texto. Obrigado!
Kai Noack #

2
O ROUNDaqui é desnecessário. assuma uma cadeia de comprimento xcom nocorrências de 'value. LENGTH(description) - LENGTH( REPLACE ( description, "value", "") ) sempre lhe dará n*length("value"), mergulhando que pelo tamanho do valor sempre deixará um número inteiro n. Não há necessidade de terminar
Nibhrit 08/10

21

Uma variação um pouco mais simples e eficaz da solução @yannis:

SELECT 
    title,
    description,    
    CHAR_LENGTH(description) - CHAR_LENGTH( REPLACE ( description, 'value', '1234') ) 
        AS `count`    
FROM <table> 

A diferença é que substituo a string "value" por uma string menor de 1 caractere ("1234" neste caso). Dessa forma, você não precisa dividir e arredondar para obter um valor inteiro.

Versão generalizada (funciona para todas as cordas da agulha):

SET @needle = 'value';
SELECT 
    description,    
    CHAR_LENGTH(description) - CHAR_LENGTH(REPLACE(description, @needle, SPACE(LENGTH(@needle)-1))) 
        AS `count`    
FROM <table> 

1
+1 para a ideia, embora geralmente prefira implementações óbvias, ou seja, que não exijam explicações adicionais, mesmo que pareçam menos elegantes.
not2savvy 8/04


12

No SQL SERVER, esta é a resposta

Declare @t table(TITLE VARCHAR(100), DESCRIPTION VARCHAR(100))

INSERT INTO @t SELECT 'test1', 'value blah blah value' 
INSERT INTO @t SELECT 'test2','value test' 
INSERT INTO @t SELECT 'test3','test test test' 
INSERT INTO @t SELECT 'test4','valuevaluevaluevaluevalue' 


SELECT TITLE,DESCRIPTION,Count = (LEN(DESCRIPTION) - LEN(REPLACE(DESCRIPTION, 'value', '')))/LEN('value') 

FROM @t

Resultado

TITLE   DESCRIPTION               Count
test1   value blah blah value        2
test2   value test                   1
test3   test test test               0
test4   valuevaluevaluevaluevalue    5

Eu não tenho o MySQL instalado, mas olhei para encontrar o equivalente de LEN é COMPRIMENTO enquanto REPLACE é o mesmo.

Portanto, a consulta equivalente no MySql deve ser

SELECT TITLE,DESCRIPTION, (LENGTH(DESCRIPTION) - LENGTH(REPLACE(DESCRIPTION, 'value', '')))/LENGTH('value') AS Count
FROM <yourTable>

Por favor, deixe-me saber se funcionou para você no MySql também.


3

Aqui está uma função que fará isso.

CREATE FUNCTION count_str(haystack TEXT, needle VARCHAR(32))
  RETURNS INTEGER DETERMINISTIC
  BEGIN
    RETURN ROUND((CHAR_LENGTH(haystack) - CHAR_LENGTH(REPLACE(haystack, needle, ""))) / CHAR_LENGTH(needle));
  END;

1
SELECT 
id,
jsondata,    
ROUND (   
    (
        LENGTH(jsondata)
        - LENGTH( REPLACE ( jsondata, "sonal", "") ) 
    ) / LENGTH("sonal")        
)
+
ROUND (   
    (
        LENGTH(jsondata)
        - LENGTH( REPLACE ( jsondata, "khunt", "") ) 
    ) / LENGTH("khunt")        
)
AS count1    FROM test ORDER BY count1 DESC LIMIT 0, 2

Obrigado Yannis, sua solução funcionou para mim e aqui estou compartilhando a mesma solução para várias palavras-chave com pedido e limite.


1

Esta é a função mysql usando a técnica de espaço (testada com o mysql 5.0 + 5.5): CREATE FUNCTION count_str( haystack TEXT, needle VARCHAR(32)) RETURNS INTEGER DETERMINISTIC RETURN LENGTH(haystack) - LENGTH( REPLACE ( haystack, needle, space(char_length(needle)-1)) );

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.