Questo è per divertimento, vero?
SQL riguarda l'elaborazione di insiemi di righe, quindi se possiamo convertire una "parola" in un insieme di caratteri come righe, possiamo utilizzare le funzioni "gruppo" per fare cose utili.
L'uso di un "motore di database relazionale" per eseguire semplici manipolazioni dei personaggi sembra sbagliato. Tuttavia, è possibile rispondere alla tua domanda solo con SQL? Sì, lo è...
Ora, ho sempre una tabella che ha una colonna intera che contiene circa 500 righe che ha la sequenza crescente 1 .. 500. Si chiama "serie intera". È un tavolo davvero piccolo che ha usato molto, quindi viene memorizzato nella cache. È progettato per sostituire il from 'select 1 ... union ...
testo nelle query.
È utile per generare righe sequenziali (una tabella) di qualsiasi cosa che puoi calcolare basata su un numero intero usandolo in un cross join
(anche qualsiasi inner join
). Lo uso per generare giorni per un anno, analizzare stringhe delimitate da virgole ecc.
Ora, sql mid
la funzione può essere utilizzata per restituire il carattere in una determinata posizione. Usando la tabella "integerseries" posso convertire "facilmente" una "parola" in una tabella di caratteri con una riga per carattere. Quindi usa le funzioni 'gruppo'...
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
Uscita:
charAtIdx count(charAtIdx)
--------- ------------------
1
d 1
e 1
H 1
l 3
o 2
r 1
W 1
Nota:il numero di righe nell'output è il numero di caratteri diversi nella stringa. Quindi, se viene contato il numero di righe di output, sarà noto il numero di "lettere diverse".
Questa osservazione viene utilizzata nella query finale.
La query finale:
Il punto interessante qui è spostare le restrizioni 'integerseries' 'cross join' (1 .. length(word)) nell'effettivo 'join' piuttosto che farlo nel where
clausola. Ciò fornisce all'ottimizzatore indizi su come limitare i dati prodotti durante l'esecuzione del 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
Uscita:
wordId word letterCount
------ -------------------- -------------
1 3333333333 1
2 1113333333 2
3 1112222444 3
4 Hello World 8
5 funny - not so much? 13
Tabella di parole e dati:
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);
Tabella delle serie intere:intervallo 1 .. 30 per questo esempio.
CREATE TABLE `integerseries` (
`id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci