Рубрики
Базы данных

Mysql: SPLIT function и правильный подсчет количества символов.

Столкнулся на днях с задачкой разделения строки состоящей из нескольких значений, разделенных некоторым разделителем, на отдельные значения на уровне базы данных. Во всех приличных БД все это уже есть из коробки, а вот в MySQL нет. Пришлось написать самому. В общем-то в нете полно решений, но во всех, что я видел, есть один большой недостаток, или даже баг. Заключается он в том, что везде требуется вычислять длину подстрок и делается везде это через функцию LENGTH(). Однако если внимательно прочитать описание этой функции, то можно узнать, что она считает длину строки в БАЙТАХ! То есть если вы используете какую-либо двухбайтную кодировку для хранения строк, например UTF-8 (* вообще станно, если вы до сих пор используете какую-то другую 🙂 *), то эта функция вернет совсем не тот результат, который вы ожидаете. Для того, чтобы избежать этой проблемы, есть другая функция CHAR_LENGTH(), которая делает именно то, что и надо — считает длину строки в символах. Что же до функции разделения строки на значения, то вот мой вариант:

CREATE FUNCTION `SPLIT_STRING`
(    str VARCHAR(2000), 
     delim VARCHAR(12), 
     pos INT ) 
RETURNS varchar(255) CHARSET utf8
COMMENT 'Разделение строки по делимитеру'
RETURN
     REPLACE(
          SUBSTRING(
               SUBSTRING_INDEX(str, delim, pos),
               CHAR_LENGTH(
                    SUBSTRING_INDEX(str, delim, pos - 1)
               ) + 1
          ),
          delim,
          ''
     )

На входе 3 параметра: сама строка, строка-разделитель и номер позиции значения. Если такого разделителя нет, вернется вся строка, позиция значения считается с 1. Вот примеры:

mysql> select SPLIT_STRING('qwe,asd,zxc', ',', 1);
+-------------------------------------+
| SPLIT_STRING('qwe,asd,zxc', ',', 1) |
+-------------------------------------+
| qwe                                 |
+-------------------------------------+
1 rows in set (0.01 sec)

mysql> select SPLIT_STRING('qwe,asd,zxc', ',', 2);
+-------------------------------------+
| SPLIT_STRING('qwe,asd,zxc', ',', 2) |
+-------------------------------------+
| asd                                 |
+-------------------------------------+
1 rows in set (0.01 sec)

mysql> select SPLIT_STRING('qwe,asd,zxc', '$', 1);
+-------------------------------------+
| SPLIT_STRING('qwe,asd,zxc', '$', 1) |
+-------------------------------------+
| qwe,asd,zxc                         |
+-------------------------------------+
1 rows in set (0.01 sec)

That’s all folks!