sql >> Databasteknik >  >> RDS >> Mysql

Räkna antalet unika tecken i en sträng

Det här är för skojs skull eller hur?

SQL handlar om att bearbeta uppsättningar av rader, så om vi kan konvertera ett "ord" till en uppsättning tecken som rader så kan vi använda "grupp"-funktionerna för att göra användbara saker.

Att använda en "relationell databasmotor" för att göra enkel karaktärsmanipulation känns fel. Är det ändå möjligt att svara på din fråga med bara SQL? Ja det är det...

Nu har jag alltid en tabell som har en heltalskolumn som har cirka 500 rader i den som har den stigande sekvensen 1 .. 500. Det kallas 'heltalsserier'. Det är en riktigt liten tabell som använts mycket så den lagras i minnet. Den är utformad för att ersätta from 'select 1 ... union ... text i frågor.

Det är användbart för att generera sekventiella rader (en tabell) av allt som du kan beräkna som är baserat på ett heltal genom att använda det i en cross join (även någon inner join ). Jag använder den för att generera dagar för ett år, analysera kommaavgränsade strängar etc.

Nu, sql mid funktion kan användas för att returnera tecknet på en given position. Genom att använda 'integerseries'-tabellen kan jag 'enkelt' konvertera ett 'ord' till en teckentabell med en rad per tecken. Använd sedan 'grupp'-funktionerna...

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  

Utdata:

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

Obs! Antalet rader i utdata är antalet olika tecken i strängen. Så om antalet utdatarader räknas kommer antalet "olika bokstäver" att vara känt.

Denna observation används i den sista frågan.

Den sista frågan:

Det intressanta här är att flytta "integerseries" "cross join"-begränsningarna (1 .. length(word)) till den faktiska "join" istället för att göra det i where klausul. Detta ger optimeraren ledtrådar om hur man begränsar data som produceras när man gör 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  

Utdata:

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

Ordtabell och data:

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

Heltalstabell:intervall 1 .. 30 för detta exempel.

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. Vad är skillnaden mellan Float och Numeric/Decimal i SQL Server - SQL Server / T-SQL Tutorial Del 33

  2. MySQL INSERT INTO med dubbla villkor för IF NOT EXIST

  3. Hur väljer man ORDER BY kolumn och RAND() båda?

  4. Konvertera kommaseparerad sträng till array i PL/SQL