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