sql >> Base de Datos >  >> RDS >> Mysql

Contar el número de caracteres únicos en una cadena

Esto es por diversión, ¿verdad?

SQL tiene que ver con el procesamiento de conjuntos de filas, por lo que si podemos convertir una 'palabra' en un conjunto de caracteres como filas, entonces podemos usar las funciones de 'grupo' para hacer cosas útiles.

Usar un 'motor de base de datos relacional' para hacer una manipulación simple de personajes se siente mal. Aún así, ¿es posible responder a su pregunta solo con SQL? Sí lo es...

Ahora, siempre tengo una tabla que tiene una columna de enteros que tiene alrededor de 500 filas que tiene la secuencia ascendente 1 .. 500. Se llama 'integerseries'. Es una tabla realmente pequeña que se usa mucho, por lo que se almacena en caché en la memoria. Está diseñado para reemplazar el from 'select 1 ... union ... texto en consultas.

Es útil para generar filas secuenciales (una tabla) de cualquier cosa que pueda calcular que se base en un número entero usándolo en una cross join (también cualquier inner join ). Lo uso para generar días durante un año, analizar cadenas delimitadas por comas, etc.

Ahora, el sql mid La función se puede utilizar para devolver el carácter en una posición determinada. Al usar la tabla de 'series enteras', puedo convertir 'fácilmente' una 'palabra' en una tabla de caracteres con una fila por carácter. Luego use las funciones de 'grupo'...

SET @word='Hello World';

SELECT charAtIdx, COUNT(charAtIdx)
FROM (SELECT charIdx.id,
    MID(@word, charIdx.id, 1) AS charAtIdx 
    FROM integerseries AS charIdx
    WHERE charIdx.id <= LENGTH(@word)
    ORDER BY charIdx.id ASC
    ) wordLetters
GROUP BY
   wordLetters.charAtIdx
ORDER BY charAtIdx ASC  

Salida:

charAtIdx  count(charAtIdx)  
---------  ------------------
                            1
d                           1
e                           1
H                           1
l                           3
o                           2
r                           1
W                           1

Nota:El número de filas en la salida es el número de caracteres diferentes en la cadena. Por lo tanto, si se cuenta el número de filas de salida, se conocerá el número de "letras diferentes".

Esta observación se utiliza en la consulta final.

La consulta final:

El punto interesante aquí es mover las restricciones de 'unión cruzada' de 'integerseries' (1 .. length(word)) a la 'unión' real en lugar de hacerlo en el where cláusula. Esto proporciona al optimizador pistas sobre cómo restringir los datos producidos al hacer join .

SELECT 
   wordLetterCounts.wordId,
   wordLetterCounts.word,   
   COUNT(wordLetterCounts.wordId) AS letterCount
FROM 
     (SELECT words.id AS wordId,
             words.word AS word,
             iseq.id AS charPos,
             MID(words.word, iseq.id, 1) AS charAtPos,
             COUNT(MID(words.word, iseq.id, 1)) AS charAtPosCount
     FROM
          words
          JOIN integerseries AS iseq
               ON iseq.id BETWEEN 1 AND words.wordlen 
      GROUP BY
            words.id,
            MID(words.word, iseq.id, 1)
      ) AS wordLetterCounts
GROUP BY
   wordLetterCounts.wordId  

Salida:

wordId  word                  letterCount  
------  --------------------  -------------
     1  3333333333                        1
     2  1113333333                        2
     3  1112222444                        3
     4  Hello World                       8
     5  funny - not so much?             13

Tabla de palabras y datos:

CREATE TABLE `words` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
  `wordlen` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

/*Data for the table `words` */

insert  into `words`(`id`,`word`,`wordlen`) values (1,'3333333333',10);
insert  into `words`(`id`,`word`,`wordlen`) values (2,'1113333333',10);
insert  into `words`(`id`,`word`,`wordlen`) values (3,'1112222444',10);
insert  into `words`(`id`,`word`,`wordlen`) values (4,'Hello World',11);
insert  into `words`(`id`,`word`,`wordlen`) values (5,'funny - not so much?',20);

Tabla de series enteras:rango 1 .. 30 para este ejemplo.

CREATE TABLE `integerseries` (
  `id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci