Строки — это там, где живут реальные запросы
С числами всё просто. А вот строки — это где запросы превращаются в кашу: имена с лишними пробелами, email в разном регистре, ID, склеенные через дефисы, поля со свободным текстом, которые почти-но-не-совсем совпадают. В SQLite есть небольшой, но хорошо продуманный набор строковых функций, который покрывает большую часть таких задач без участия прикладного кода.
На этой странице разберём те, к которым тянется рука первым делом: конкатенация, вырезание подстрок, поиск, замена, обрезка пробелов и форматирование.
Конкатенация строк в SQLite — это ||, а не CONCAT
В SQLite нет функции CONCAT. Строки склеиваются оператором ||:
Числа и значения других типов сами приводятся к тексту. Но есть нюанс: если хотя бы один операнд — NULL, всё выражение тоже превращается в NULL. Это стандартное поведение SQL, но многих оно застаёт врасплох:
Оборачивайте nullable-столбцы в COALESCE(col, '') или COALESCE(col, 'default'), если не хотите, чтобы одно пропущенное значение обнулило всю строку.
LENGTH, UPPER, LOWER
Эта тройка пригодится вам буквально каждый день:
LENGTH возвращает количество символов в тексте, а не байт. Если вам нужны именно байты (редкий случай, но полезный для анализа объёма хранения), используйте OCTET_LENGTH. Функции UPPER и LOWER по умолчанию преобразуют только ASCII-буквы — символы с диакритикой остаются без изменений, пока вы не подключите расширение ICU.
SUBSTR: вырезаем подстроку
Функция SUBSTR(text, start, length) извлекает фрагмент строки. Индексация в SQLite начинается с единицы — 1 означает первый символ, а не 0:
Несколько моментов, которые стоит запомнить:
- Третий аргумент необязателен. Если его не указать, вернётся всё от позиции
startдо конца строки. - Отрицательный
startотсчитывается от конца строки. - Если
startоказался за пределами строки, вы получите пустую строку, а не ошибку.
В качестве синонима принимается SUBSTRING — пригодится, если мышечная память тянет вас к другой СУБД.
INSTR: поиск подстроки в SQLite
INSTR(haystack, needle) возвращает позицию первого вхождения needle в haystack (нумерация с единицы) или 0, если подстрока не найдена:
Это классическая идиома SQLite для «всего, что идёт до @»: находим разделитель через INSTR, а затем вырезаем нужный кусок с помощью SUBSTR. Такую связку вы будете писать постоянно. Учтите один момент: если совпадений нет, INSTR возвращает 0, и перед вырезанием это стоит проверить — передача 0 в SUBSTR молча выдаёт странный результат.
REPLACE: замена одной подстроки на другую
Функция REPLACE(text, old, new) заменяет каждое вхождение old на new:
Учтите: функция чувствительна к регистру и регулярные выражения не понимает — только точное вхождение подстроки. Для более сложных преобразований можно сцепить несколько REPLACE подряд, но если вложенность переваливает за два-три уровня — пора выносить логику в приложение.
TRIM, LTRIM, RTRIM
Данные, которые ввёл пользователь, частенько приходят с пробелами по краям. TRIM их подчищает:
По умолчанию они убирают пробелы. Если нужно вырезать другие символы, передайте их вторым аргументом — причём каждый символ из этого аргумента воспринимается как элемент «множества для удаления», а не как цельная подстрока. Например, TRIM('xxxhelloxx', 'x') вернёт 'hello'.
printf: форматирование чисел и строк
Когда нужна красиво оформленная строка — фиксированное число знаков после запятой, выравнивание чисел нулями или вывод в шестнадцатеричном виде — на помощь приходит printf (он же format):
Спецификаторы формата здесь — те же, что в C: %d, %s, %f, %x, дополнение нулями или пробелами и так далее. Получается куда чище, чем городить строку через || и кучу CAST-ов.
LIKE vs GLOB: поиск по шаблону
Два оператора — два разных мира.
LIKE работает с классическими SQL-подстановочными символами: % — любая последовательность символов, _ — один символ. Для ASCII регистр не учитывается:
GLOB работает с шаблонами в стиле Unix shell: * — любая последовательность символов, ? — ровно один символ, [abc] — класс символов. При этом регистр учитывается:
Правило выбора простое: LIKE — для «человеческих» запросов в духе «начинается с», «содержит», «заканчивается на». GLOB — когда важен регистр или нужны классы символов. Оба умеют пользоваться индексом, но только если шаблон привязан к началу строки ('foo%', а не '%foo'): ведущий wildcard заставляет SQLite пройтись по всей таблице.
Как разбить строку в SQLite: функции SPLIT здесь нет
В SQLite нет встроенной функции SPLIT_STRING. На практике обходятся двумя способами:
Если нужно разбить строку по разделителю на несколько строк результата, самый чистый вариант — это json_each поверх JSON-массива или рекурсивный CTE. К обоим подходам мы ещё вернёмся в следующих главах, а пока просто держите в голове: в SQLite «дай мне каждое слово» одной строчкой не пишется.
Боевой пример: чистим имена пользователей
Соберём всё вместе. Допустим, есть таблица users с неаккуратными отображаемыми именами: лишние пробелы, разнобой в регистре, плюс опциональные приставки вроде "Dr. " или "Mr. ", от которых хочется избавиться:
Выражение читается изнутри наружу: сначала срезаем пробелы по краям, переводим в нижний регистр, убираем обращения, а затем снова применяем TRIM — на случай, если после удаления обращения остался ведущий пробел. Каждый шаг — это одна функция, а сложность возникает только из-за их вложенности. Если стопка разрослась до четырёх и больше уровней — это уже сигнал: либо завести вычисляемую колонку (см. главу «Продвинутые возможности»), либо очищать данные ещё на этапе импорта.
Что стоит запомнить
||склеивает строки;NULLотравляет результат, поэтому подстраховывайтесь черезCOALESCE.- Связка
SUBSTRиINSTRзакрывает большинство задач в духе «найди и отрежь». REPLACEзаменяет все вхождения указанной подстроки.TRIMи его собратья умеют работать не только с пробелами — им можно передать свой набор символов.printf— правильный инструмент, когда нужен форматированный вывод.LIKE— это SQL-шаблоны без учёта регистра,GLOB— шаблоны в стиле шелла с учётом регистра.
Дальше: числовые функции
Со строками разобрались — логично перейти к числам: округление, модуль, особенности деления и математические функции, которые появились в SQLite в последних версиях. Об этом — на следующей странице.
Часто задаваемые вопросы
Как склеить строки в SQLite?
Через оператор ||, а не CONCAT — функции CONCAT в SQLite попросту нет. Например, 'Привет, ' || name соединит две строки в одну. Важный момент: если хотя бы один из операндов равен NULL, результат тоже будет NULL. Так что если столбец может быть пустым, оборачивайте его в COALESCE.
Как взять подстроку в SQLite?
Используйте SUBSTR(text, start, length) (синоним — SUBSTRING). Нумерация начинается с 1, так что SUBSTR('hello', 1, 3) вернёт 'hel'. Если передать отрицательный start, отсчёт пойдёт с конца строки. Аргумент length необязательный — без него вернётся всё до конца строки.
Есть ли в SQLite функция SPLIT_STRING?
Нет, встроенной функции для разбиения строки в SQLite не предусмотрено. В большинстве случаев нужный кусок можно вытащить связкой INSTR + SUBSTR, либо использовать рекурсивный CTE для разделения по разделителю. Если такая задача встречается часто, удобнее всего хранить данные как JSON-массив и проходить по ним через json_each — это чище, чем городить свой сплиттер.
Чем отличаются LIKE и GLOB в SQLite?
LIKE по умолчанию не учитывает регистр для ASCII-символов и работает с шаблонами % и _. GLOB, наоборот, чувствителен к регистру и использует Unix-подобные шаблоны (*, ?, [abc]). Берите GLOB, когда важен регистр или нужны классы символов; LIKE подойдёт для привычного SQL-стиля поиска по шаблону.