Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Contar o número de caracteres exclusivos em uma string


Isso é para se divertir certo?

SQL tem tudo a ver com o processamento de conjuntos de linhas, portanto, se pudermos converter uma 'palavra' em um conjunto de caracteres como linhas, podemos usar as funções de 'grupo' para fazer coisas úteis.

Usar um 'mecanismo de banco de dados relacional' para fazer manipulação simples de caracteres parece errado. Ainda assim, é possível responder à sua pergunta apenas com SQL? É sim...

Agora, eu sempre tenho uma tabela que tem uma coluna inteira que tem cerca de 500 linhas que tem a seqüência ascendente 1 .. 500. Ela é chamada de 'integerseries'. É uma tabela muito pequena que usou muito, por isso fica armazenada em cache na memória. Ele é projetado para substituir o from 'select 1 ... union ... texto nas consultas.

É útil para gerar linhas sequenciais (uma tabela) de qualquer coisa que você possa calcular com base em um inteiro usando-o em uma cross join (também qualquer inner join ). Eu o uso para gerar dias por um ano, analisar strings delimitadas por vírgulas etc.

Agora, o sql mid A função pode ser usada para retornar o caractere em uma determinada posição. Usando a tabela 'integerseries' eu posso 'facilmente' converter uma 'palavra' em uma tabela de caracteres com uma linha por caractere. Em seguida, use as funções '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  

Saída:
charAtIdx  count(charAtIdx)  
---------  ------------------
                            1
d                           1
e                           1
H                           1
l                           3
o                           2
r                           1
W                           1

Nota:O número de linhas na saída é o número de caracteres diferentes na string. Portanto, se o número de linhas de saída for contado, o número de 'letras diferentes' será conhecido.

Essa observação é usada na consulta final.

A consulta final:

O ponto interessante aqui é mover as restrições de 'cross join' 'integerseries' (1 .. length(word)) para o 'join' real em vez de fazê-lo no where cláusula. Isso fornece ao otimizador dicas sobre como restringir os dados produzidos ao fazer o 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  

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

Tabela de palavras e dados:
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);

Tabela de séries inteiras:intervalo 1 .. 30 para este exemplo.
CREATE TABLE `integerseries` (
  `id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci