Перейти к содержанию
Educora
Продвинутый20 мин16 / 22

Функции для строк, дат и чисел

Обрезай, склеивай и ищи текст, считай с датами и округляй числа в SQLite, а заодно узнай аналоги каждой функции в MySQL и PostgreSQL.

Проверь себя
В этом уроке ты узнаешь
  • Обрабатывать текст с помощью SUBSTR, INSTR, REPLACE, UPPER и LENGTH
  • Разбирать и сдвигать даты и считать дни между ними с помощью date, strftime и julianday
  • Правильно использовать целочисленное деление, остаток и округление и переносить функции в другие диалекты

Данные редко хранятся именно в том виде, который нам нужен. Выделить домен из e-mail, сделать короткое имя вроде «A. Məmmədova», вычислить крайний срок оплаты, добавить к цене НДС и округлить — всё это можно сделать в коде программы, но часто проще и быстрее прямо в запросе. В этом уроке мы разберём три группы функций. Внимание: функции — самая «диалектная» часть SQL, поэтому для каждой мы приводим аналог в MySQL и PostgreSQL.

Строковые функции

  • LENGTH(s) — число символов; UPPER(s), LOWER(s) — заглавные и строчные буквы.
  • SUBSTR(s, начало, длина) — часть текста; позиции считаются с 1, без длины берётся до конца.
  • INSTR(s, подстрока) — первая позиция подстроки (0, если её нет); REPLACE(s, старое, новое) — замена; TRIM(s) — убирает пробелы по краям.
SQL
SELECT UPPER(last_name) AS upper_name,
       LENGTH(last_name) AS len,
       SUBSTR(first_name, 1, 1) || '. ' || last_name AS short_name,
       SUBSTR(email, INSTR(email, '@') + 1) AS domain
FROM students
WHERE id IN (1, 3, 4)
ORDER BY id;
▸ Ожидаемый результат
upper_name | len | short_name | domain
MəMMəDOVA | 9 | A. Məmmədova | example.com
HüSEYNOVA | 9 | L. Hüseynova | example.com
QULIYEV | 7 | E. Quliyev | NULL
У Эльвина нет e-mail: любая функция от NULL снова даёт NULL.

Функции для дат

В SQLite нет отдельного типа для дат: даты хранятся как текст в формате YYYY-MM-DD. strftime(формат, дата) извлекает части даты (%Y — год, %m — месяц, %w — день недели, 0 = воскресенье), а date(дата, модификатор...) сдвигает дату: '+30 days', '-1 month', 'start of month'.

SQL
SELECT id, order_date,
       strftime('%Y', order_date) AS year,
       strftime('%m', order_date) AS month,
       strftime('%w', order_date) AS weekday,
       date(order_date, '+30 days') AS pay_until
FROM orders
WHERE id <= 4
ORDER BY id;
▸ Ожидаемый результат
id | order_date | year | month | weekday | pay_until
1 | 2025-01-15 | 2025 | 01 | 3 | 2025-02-14
2 | 2025-01-15 | 2025 | 01 | 3 | 2025-02-14
3 | 2025-02-02 | 2025 | 02 | 0 | 2025-03-04
4 | 2025-02-10 | 2025 | 02 | 1 | 2025-03-12

Чтобы посчитать дни между двумя датами, превращаем их в числа с помощью julianday — число дней от фиксированной начальной точки — и вычитаем. Ниже мы находим, сколько дней прошло у каждого покупателя от регистрации до первого заказа.

SQL
SELECT c.name, c.joined_on,
       MIN(o.order_date) AS first_order,
       CAST(julianday(MIN(o.order_date)) - julianday(c.joined_on) AS INTEGER)
         AS days_to_first_order
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.id, c.name, c.joined_on
ORDER BY days_to_first_order, c.name;
▸ Ожидаемый результат
name | joined_on | first_order | days_to_first_order
Zəhra Hüseynli | 2025-02-14 | 2025-04-09 | 54
Mehmet Kaya | 2025-04-01 | 2025-06-12 | 72
John Carter | 2024-08-30 | 2025-03-18 | 200
Olga Ivanova | 2024-06-18 | 2025-03-05 | 260
Emre Yılmaz | 2024-05-05 | 2025-02-10 | 281
Lalə Əhmədova | 2024-03-22 | 2025-02-02 | 317
Anar Mustafayev | 2024-01-10 | 2025-01-15 | 371

Математические функции и округление

Деление двух целых чисел в SQLite, PostgreSQL и SQL Server снова даёт целое: 7 / 2 = 3, а % даёт остаток. Это полезно в задачах вроде упаковки: 500 тетрадей в коробках по 24 — это 20 полных коробок и 20 тетрадей в остатке. Добавляя к цене НДС 18%, округляем результат до гяпиков с помощью ROUND(x, 2).

SQL
SELECT name, stock,
       stock / 24 AS full_boxes,
       stock % 24 AS left_over,
       ROUND(price * 1.18, 2) AS price_with_vat
FROM products
WHERE category IN ('Stationery', 'Accessories')
ORDER BY id;
▸ Ожидаемый результат
name | stock | full_boxes | left_over | price_with_vat
Notebook | 500 | 20 | 20 | 3.78
Pen set | 300 | 12 | 12 | 9.32
Backpack | 60 | 2 | 12 | 64.9
Water bottle | 120 | 5 | 0 | 14.16
SQL
SELECT 7 / 2               AS int_div,
       7 / 2.0             AS real_div,
       ROUND(2.5)          AS half_up,
       ROUND(-2.5)         AS half_down,
       CAST(-12.9 AS INTEGER) AS truncated;
▸ Ожидаемый результат
int_div | real_div | half_up | half_down | truncated
3 | 3.5 | 3 | -3 | -12
ROUND округляет половины от нуля, а CAST(... AS INTEGER) просто отбрасывает дробную часть.

Математические функции вроде SQRT, POWER, FLOOR, CEIL и LN в PostgreSQL и MySQL есть всегда. В SQLite их добавили в версии 3.35, но они доступны только в сборках со специальным параметром, поэтому переносимым запросам для SQLite надёжнее обходиться ROUND, ABS, CAST и %.

«Словарь» между диалектами

ЗадачаSQLiteMySQLPostgreSQL
Число символовLENGTH(s)CHAR_LENGTH(s)LENGTH(s)
Часть строкиSUBSTR(s, 2, 3)SUBSTRING(s, 2, 3)SUBSTRING(s FROM 2 FOR 3)
Позиция подстрокиINSTR(s, '@')LOCATE('@', s)POSITION('@' IN s)
Склейкаa || bCONCAT(a, b)a || b
Год датыstrftime('%Y', d)YEAR(d)EXTRACT(YEAR FROM d)
Прибавить 30 днейdate(d, '+30 days')DATE_ADD(d, INTERVAL 30 DAY)d + INTERVAL '30 days'
Дни между датамиjulianday(b) - julianday(a)DATEDIFF(b, a)b - a
Начало месяцаdate(d, 'start of month')DATE_FORMAT(d, '%Y-%m-01')date_trunc('month', d)
Сегодняшняя датаdate('now')CURDATE()CURRENT_DATE
В MySQL LENGTH считает не символы, а байты: LENGTH('ə') даёт 2. В PostgreSQL b - a возвращает число дней только для столбцов типа DATE.
Задание

Для каждого ученика с e-mail выведи часть адреса до знака @ (login) и имя (first_name). Отсортируй по login.

Задание · SQL
SELECT email, first_name
FROM students
WHERE email IS NOT NULL
ORDER BY email;
▸ Ожидаемый результат
login | first_name
aysel | Aysel
fidan | Fidan
gunay | Günay
kamran | Kamran
leyla | Leyla
murad | Murad
nigar | Nigar
orxan | Orxan
rashad | Rəşad
sabina | Səbinə
Задание

Для каждого месяца (month, например 2025-02) выведи число заказов (orders) и последний день этого месяца (month_end). Отсортируй по месяцу.

Задание · SQL
SELECT SUBSTR(order_date, 1, 7) AS month,
       COUNT(*) AS orders
       -- add month_end
FROM orders
GROUP BY month
ORDER BY month;
▸ Ожидаемый результат
month | orders | month_end
2025-01 | 2 | 2025-01-31
2025-02 | 2 | 2025-02-28
2025-03 | 2 | 2025-03-31
2025-04 | 2 | 2025-04-30
2025-05 | 1 | 2025-05-31
2025-06 | 2 | 2025-06-30
2025-07 | 1 | 2025-07-31

Главное

  • SUBSTR, INSTR, REPLACE, TRIM и || обрезают, ищут и склеивают текст; позиции в SQLite считаются с 1.
  • UPPER/LOWER в SQLite меняют только A–Z; в MySQL LENGTH считает байты.
  • strftime разбирает дату на части, date(d, '+30 days') сдвигает её, а разность julianday считает дни.
  • Деление целых даёт целое (7 / 2 = 3), % — остаток, ROUND(x, 2) округляет до сотых.
  • Функции сильнее всего различаются между диалектами — при переходе на другую СУБД сверяйся с таблицей соответствий.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Что вернёт SUBSTR('Bakı', 2, 2)?