sql >> Database >  >> RDS >> Mysql

Tel het aantal unieke tekens in een string

Dit is toch voor de lol?

Bij SQL draait het allemaal om het verwerken van reeksen rijen, dus als we een 'woord' kunnen omzetten in een reeks tekens als rijen, kunnen we de 'groeps'-functies gebruiken om nuttige dingen te doen.

Het gebruik van een 'relationele database-engine' om eenvoudige karaktermanipulatie uit te voeren, voelt verkeerd. Is het echter mogelijk om uw vraag te beantwoorden met alleen SQL? Ja, dat is het...

Nu heb ik altijd een tabel met één geheeltallige kolom met ongeveer 500 rijen erin en met de oplopende reeks 1 .. 500. Het wordt 'integerseries' genoemd. Het is een heel kleine tabel die veel wordt gebruikt, dus het wordt in het geheugen opgeslagen. Het is ontworpen om de from 'select 1 ... union ... . te vervangen tekst in zoekopdrachten.

Het is handig voor het genereren van opeenvolgende rijen (een tabel) van alles wat u kunt berekenen en dat is gebaseerd op een geheel getal door het te gebruiken in een cross join (ook elke inner join ). Ik gebruik het voor het genereren van dagen voor een jaar, het ontleden van door komma's gescheiden tekenreeksen enz.

Nu, de sql mid functie kan worden gebruikt om het teken op een bepaalde positie te retourneren. Door gebruik te maken van de 'integerseries' tabel kan ik 'gemakkelijk' een 'woord' omzetten in een karaktertabel met één rij per karakter. Gebruik dan de 'groep'-functies...

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  

Uitgang:

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

Opmerking:het aantal rijen in de uitvoer is het aantal verschillende tekens in de tekenreeks. Dus als het aantal uitvoerrijen wordt geteld, is het aantal 'verschillende letters' bekend.

Deze observatie wordt gebruikt in de laatste vraag.

De laatste vraag:

Het interessante punt hier is om de 'integerseries' 'cross join'-beperkingen (1 .. length(word)) naar de daadwerkelijke 'join' te verplaatsen in plaats van dit te doen in de where clausule. Dit geeft de optimizer aanwijzingen over hoe de gegevens die worden geproduceerd bij het uitvoeren van de join . kunnen worden beperkt .

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  

Uitgang:

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

Woordtabel en gegevens:

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);

Tabel met gehele getallen:bereik 1 .. 30 voor dit voorbeeld.

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


  1. Databaseverbinding Mysql ontbreekt en mysql.sock ontbreekt

  2. Hoe verwerkt MySQL ORDER BY en LIMIT in een query?

  3. syntaxisfout bij het declareren van variabelen in een pl/sql-procedure

  4. Kan ik WHERE col LIKE '%' ook NULL-waarden laten selecteren?