¿Cuenta el número de ocurrencias de una cadena en un campo VARCHAR?


175

Tengo una mesa como esta:

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

Estoy tratando de descubrir cómo devolver la cantidad de veces que ocurre una cadena en cada una de las DESCRIPCIONES.

Entonces, si quiero contar la cantidad de veces que aparece 'value', la instrucción sql devolverá esto:

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

¿Hay alguna forma de hacer esto? No quiero usar php en absoluto, solo mysql.


44
Las respuestas a continuación lo llevarán allí. Sin embargo, no olvide usar en CHAR_LENGTH()lugar de LENGTH()si está usando caracteres multibyte.
inhan

Este hilo también ha sido respondido aquí
Delickate

Hola, ¿cómo hago esto con la consulta sqlserver?
aintno12u

LENGTH ([field]) - LENGTH (REPLACE ([field], '[char_to_find]', ''))
Phoenix

Respuestas:


343

Esto debería funcionar:

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

55
¡Esta solución es increíble, justo lo que necesitaba! Pero tenga en cuenta que LENGTH () no es seguro para varios bytes y puede encontrar errores extraños. Use CHAR_LENGTH () en su lugar :)
nico gawenda

1
no hay diferencia en el uso de LENGTH()y CHAR_LENGTH()mientras se divide en el mismo byte de conteo / char. @nicogawenda
MohaMad

3
@chyupa undevaluetiene valueen él, por lo que debe contarse. Si solo desea contar palabras completas, tal vez necesite buscar 'valor' o mejor algo más complicado como usar expresiones regulares.
PhoneixS

2
Tenga en cuenta que se encuentra con recuentos incorrectos cuando busca en el texto que también tiene palabras con letras mayúsculas (como el alemán, donde todos los sustantivos se escriben con letras mayúsculas). El REEMPLAZO solo reemplaza las coincidencias exactas. Para considerar todas las palabras, debe cambiar el reemplazo anterior a: LENGTH( REPLACE ( LOWER(description), "value", "") )y asegurarse de que "valor" siempre esté en minúscula utilizando PHP strtolower(). PD: Esta solución anterior me ayudó a construir mi propio pequeño motor de búsqueda y a ponderar los resultados por la cantidad de palabras dentro del texto. ¡Gracias!
Kai Noack

2
El ROUNDaquí es innecesario. asumir una cadena de longitud xcon nocurrencias de 'value. LENGTH(description) - LENGTH( REPLACE ( description, "value", "") ) siempre te dará n*length("value"), bucear que por longitud de valor siempre dejará un número entero n. No hay necesidad de redondear
Nibhrit

21

Una variación un poco más simple y más efectiva de la solución @yannis:

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

La diferencia es que reemplazo la cadena de "valor" con una cadena más corta de 1 carácter ("1234" en este caso). De esta manera, no necesita dividir y redondear para obtener un valor entero.

Versión generalizada (funciona para cada hilo de aguja):

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

1
+1 para la idea, aunque generalmente prefiero implementaciones obvias, es decir, que no requieren una explicación adicional, incluso si se ven menos elegantes.
not2savvy


12

En SQL SERVER, esta es la respuesta

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

No tengo la instalación de MySQL, pero las gafas para encontrar el equivalente de LEN es LONGITUD mientras que REPLACE es igual.

Entonces la consulta equivalente en MySql debería ser

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

Avíseme si también funcionó para usted en MySql.


3

Aquí hay una función que hará eso.

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

Gracias Yannis, su solución funcionó para mí y aquí estoy compartiendo la misma solución para varias palabras clave con orden y límite.


1

Esta es la función mysql que usa la técnica espacial (probada con 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)) );

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.