To dla zabawy, prawda?
SQL polega na przetwarzaniu zestawów wierszy, więc jeśli możemy przekonwertować „słowo” na zestaw znaków jako wiersze, możemy użyć funkcji „grupy”, aby zrobić przydatne rzeczy.
Używanie „silnika relacyjnej bazy danych” do wykonywania prostych manipulacji znakami wydaje się niewłaściwe. Czy jednak można odpowiedzieć na twoje pytanie za pomocą samego SQL? Tak, to jest...
Teraz zawsze mam tabelę, która ma jedną kolumnę liczb całkowitych, która ma około 500 wierszy i ma kolejność rosnącą od 1 do 500. Nazywa się to „seriami całkowitymi”. Jest to naprawdę mały stół, który dużo używał, więc jest buforowany w pamięci. Został zaprojektowany w celu zastąpienia from 'select 1 ... union ...
tekst w zapytaniach.
Przydaje się do generowania kolejnych wierszy (tabeli) wszystkiego, co można obliczyć na podstawie liczby całkowitej, używając jej w cross join
(także dowolne inner join
). Używam go do generowania dni przez rok, analizowania ciągów oddzielonych przecinkami itp.
Teraz sql mid
funkcja może być użyta do zwrócenia znaku na danej pozycji. Używając tabeli „całkowite” mogę „łatwo” przekonwertować „słowo” na tabelę znaków z jednym wierszem na znak. Następnie użyj funkcji 'grupy'...
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
Wyjście:
charAtIdx count(charAtIdx)
--------- ------------------
1
d 1
e 1
H 1
l 3
o 2
r 1
W 1
Uwaga:Liczba wierszy w danych wyjściowych to liczba różnych znaków w ciągu. Tak więc, jeśli policzona zostanie liczba wierszy wyjściowych, to liczba „różnych liter” będzie znana.
Ta obserwacja jest używana w ostatecznym zapytaniu.
Ostateczne zapytanie:
Interesującym punktem jest przeniesienie ograniczeń 'łączenia krzyżowego' 'integereries' (1 .. length(word)) do rzeczywistego 'join' zamiast robienia tego w where
klauzula. Daje to optymalizatorowi wskazówki, jak ograniczyć dane generowane podczas wykonywania 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
Wyjście:
wordId word letterCount
------ -------------------- -------------
1 3333333333 1
2 1113333333 2
3 1112222444 3
4 Hello World 8
5 funny - not so much? 13
Tabela słów i dane:
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);
Tabela szeregów całkowitych:zakres 1 .. 30 dla tego przykładu.
CREATE TABLE `integerseries` (
`id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci