Това е за забавление, нали?
SQL е свързан с обработката на набори от редове, така че ако можем да преобразуваме „дума“ в набор от знаци като редове, тогава можем да използваме функциите „група“, за да правим полезни неща.
Използването на „двигател на релационна база данни“ за извършване на проста манипулация на знаци е погрешно. И все пак, възможно ли е да се отговори на въпроса ви само с SQL? Да, така е...
Сега винаги имам таблица, която има една целочислена колона, която има около 500 реда в нея, която има възходяща последователност 1 .. 500. Нарича се „целочислени серии“. Това е наистина малка таблица, която използва много, така че се кешира в паметта. Той е предназначен да замени from 'select 1 ... union ...
текст в заявките.
Полезно е за генериране на последователни редове (таблица) от всичко, което можете да изчислите, което се основава на цяло число, като го използвате в cross join
(също и всяко inner join
). Използвам го за генериране на дни за една година, анализиране на низове, разделени със запетая и т.н.
Сега sql mid
функцията може да се използва за връщане на символа на дадена позиция. С помощта на таблицата 'integerseries' мога 'лесно' да преобразувам 'дума' в таблица със знаци с един ред на знак. След това използвайте функциите "група"...
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
Изход:
charAtIdx count(charAtIdx)
--------- ------------------
1
d 1
e 1
H 1
l 3
o 2
r 1
W 1
Забележка:Броят на редовете в изхода е броят на различните символи в низа. Така че, ако броят на изходните редове се преброи, тогава броят на „различните букви“ ще бъде известен.
Това наблюдение се използва в крайната заявка.
Последната заявка:
Интересният момент тук е да преместите ограниченията за кръстосано присъединяване (1 .. length(word)) на 'integerseries' в действителното 'join', вместо да го правите в where
клауза. Това предоставя на оптимизатора улики как да ограничи данните, произведени при извършване на 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
Изход:
wordId word letterCount
------ -------------------- -------------
1 3333333333 1
2 1113333333 2
3 1112222444 3
4 Hello World 8
5 funny - not so much? 13
Таблица с думи и данни:
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);
Таблица с цели серии:диапазон 1 .. 30 за този пример.
CREATE TABLE `integerseries` (
`id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci