Базы данных | Практическое занятие 8
Создание запросов с использованием стандартных функций в PostgreSQL
1. Введение в стандартные функции
Стандартные функции (также известные как встроенные функции) — это предопределенные операции в языке SQL, которые выполняют определенные задачи со значениями данных. Они принимают ноль или более входных аргументов и возвращают одно значение. Использование этих функций может значительно упростить обработку и извлечение данных, позволяя форматировать вывод, выполнять вычисления, обрабатывать значения NULL и извлекать определенные части информации непосредственно в вашем запросе.
В PostgreSQL существует обширная библиотека стандартных функций, охватывающих различные типы данных: строки, числа, даты/время и другие. Мы рассмотрим некоторые из наиболее часто используемых.
Зачем использовать стандартные функции?
- Преобразование данных: Изменение данных из одного формата в другой (например, текст в верхний регистр, округление чисел).
- Извлечение данных: Получение определенных частей из значения данных (например, год из даты, подстрока из текста).
- Вычисление: Выполнение математических операций.
- Условная логика: Реализация простой логики if-then-else внутри запроса.
- Эффективность: Часто операции выполняются более эффективно в движке базы данных, чем обработка необработанных данных во внешнем приложении.
2. Общие категории стандартных функций
Рассмотрим функции по категориям, используя гипотетические таблицы employees
(со столбцами employee_id
, first_name
, last_name
, hire_date
, salary
) и products
(со столбцами product_id
, product_name
, price
, stock_date
).
-- Удалить таблицы, если они существуют, чтобы начать с чистого листа (необязательно, использовать с осторожностью)
-- DROP TABLE IF EXISTS products;
-- DROP TABLE IF EXISTS employees;
-- 1. Создание таблицы 'employees'
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY, -- SERIAL автоматически создает автоинкрементное целое число
first_name VARCHAR(50) NOT NULL,
middle_name VARCHAR(50), -- Разрешает NULL, используется в примере COALESCE
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE, -- Добавлено для примера LOWER(), UNIQUE гарантирует отсутствие дубликатов
hire_date DATE,
salary NUMERIC(10, 2) -- Точность 10, Масштаб 2 (например, 12345678.99)
);
-- 2. Вставка образцов данных в таблицу 'employees'
INSERT INTO employees (first_name, middle_name, last_name, email, hire_date, salary) VALUES
('Alice', 'Jane', 'Smith', 'alice.j.smith@company.com', '2020-05-15', 60000.00),
('Bob', NULL, 'Johnson', 'B.Johnson@company.com', '2019-11-01', 75000.50),
('Charlie', 'Robert', 'Williams', 'charlie.w@company.com', '2021-01-20', 55000.00),
('Diana', NULL, 'Brown', 'diana.brown@company.com', '2020-05-15', 62000.75),
('Ethan', 'Michael', 'Davis', 'Ethan.M.Davis@company.com', '2022-08-30', 80000.00),
('Fiona', 'Grace', 'Miller', 'fiona.miller@company.com', '2023-12-10', 50000.00); -- Принят на работу в декабре
-- 3. Создание таблицы 'products'
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
product_code VARCHAR(20) UNIQUE, -- Добавлено для примера TRIM()
price NUMERIC(8, 2), -- Точность 8, Масштаб 2 (например, 123456.78)
stock_date DATE
);
-- 4. Вставка образцов данных в таблицу 'products'
INSERT INTO products (product_name, product_code, price, stock_date) VALUES
('Laptop Pro 15', ' LP15-001 ', 1499.99, '2023-01-20'), -- Обратите внимание на начальные/конечные пробелы в коде
('Wireless Mouse', 'WM-100A', 25.50, '2023-02-15'),
('Mechanical Keyboard', ' MK-X2000', 120.00, '2023-01-20'), -- Обратите внимание на начальный пробел в коде
('USB-C Hub', 'UCH-05B', 45.95, '2023-03-01'),
('Monitor 27 Inch', 'MON27-QHD ', 350.00, '2022-11-10'), -- Обратите внимание на конечный пробел в коде
('Webcam HD', 'WC-1080P', 75.25, '2023-02-15'),
('External SSD 1TB', 'SSD-EXT1T', 99.99, '2023-04-05');
2.1 Функции работы со строками
Эти функции работают с текстовыми данными (типы VARCHAR
, TEXT
, CHAR
).
LENGTH(string)
: Возвращает количество символов в строке.-- Найти длину фамилии каждого сотрудника SELECT last_name, LENGTH(last_name) AS name_length FROM employees;
UPPER(string)
/LOWER(string)
: Преобразует строку в верхний или нижний регистр.-- Отобразить названия продуктов в верхнем регистре SELECT UPPER(product_name) AS upper_product_name FROM products; -- Отобразить адреса электронной почты (предполагается наличие столбца 'email') в нижнем регистре SELECT LOWER(email) AS lower_email FROM employees;
SUBSTRING(string FROM start [FOR length])
: Извлекает подстроку. Отсчет позиции начинается с 1.-- Получить первые 3 символа имени сотрудника SELECT first_name, SUBSTRING(first_name FROM 1 FOR 3) AS first_initials FROM employees;
CONCAT(str1, str2, ...)
или оператор||
: Объединяет (соединяет) две или более строки. Оператор||
часто предпочтительнее в PostgreSQL из-за его простоты.-- Создать строку с полным именем из имени и фамилии SELECT first_name, last_name, CONCAT(first_name, ' ', last_name) AS full_name_concat FROM employees; -- Использование оператора || (рекомендуется в PostgreSQL) SELECT first_name, last_name, first_name || ' ' || last_name AS full_name_operator FROM employees;
REPLACE(string, from_substring, to_substring)
: Заменяет все вхождения подстроки в строке другой подстрокой.-- Заменить пробелы на подчеркивания в названиях продуктов (для примера использования) SELECT product_name, REPLACE(product_name, ' ', '_') AS underscored_name FROM products;
TRIM([LEADING | TRAILING | BOTH] [characters] FROM string)
: Удаляет начальные, конечные или оба (начальные и конечные) символа (по умолчанию - пробелы) из строки.-- Предположим, что столбец 'product_code' может содержать начальные/конечные пробелы SELECT product_code, TRIM(product_code) AS trimmed_code FROM products; -- Удалить начальные нули из кода SELECT '00123', TRIM(LEADING '0' FROM '00123'); -- Возвращает '123'
2.2 Числовые функции
Эти функции выполняют вычисления над числовыми типами данных (INTEGER
, NUMERIC
, DECIMAL
, REAL
, DOUBLE PRECISION
и т.д.).
ABS(number)
: Возвращает абсолютное (неотрицательное) значение числа.-- Вычислить разницу между зарплатой каждого сотрудника и целевым показателем в $65,000. -- Затем отобразить абсолютное значение этой разницы, используя ABS(). SELECT employee_id, first_name, salary, salary - 65000.00 AS difference_from_target, -- Это может быть отрицательным ABS(salary - 65000.00) AS absolute_difference -- Это всегда будет положительным FROM employees;
ROUND(number [, decimal_places])
: Округляет число до указанного количества знаков после запятой. Еслиdecimal_places
опущено, округляет до ближайшего целого числа.-- Округлить цены на товары до 1 знака после запятой SELECT product_name, price, ROUND(price, 1) AS rounded_price FROM products; -- Округлить зарплаты до ближайшей 1000 (используя отрицательное значение decimal_places) SELECT salary, ROUND(salary, -3) AS rounded_salary_k FROM employees;
CEIL(number)
илиCEILING(number)
: Возвращает наименьшее целое число, большее или равное заданному числу (округляет вверх).-- Демонстрация CEIL: Показать цену продукта, округленную ВВЕРХ до ближайшей целой суммы в долларах SELECT product_name, price, CEIL(price) AS price_rounded_up_dollar -- Вы также можете использовать CEILING(price), который делает то же самое FROM products;
FLOOR(number)
: Возвращает наибольшее целое число, меньшее или равное заданному числу (округляет вниз).-- Найти целую часть цены в долларах SELECT price, FLOOR(price) AS whole_dollars FROM products;
MOD(dividend, divisor)
или оператор%
: Возвращает остаток от деления (операция взятия по модулю).-- Определить, является ли ID сотрудника четным или нечетным SELECT employee_id, MOD(employee_id, 2) AS remainder -- 0 для четного, 1 для нечетного FROM employees; -- Использование оператора % SELECT employee_id, employee_id % 2 AS remainder FROM employees;
2.3 Функции даты/времени
Эти функции работают с типами данных даты и времени (DATE
, TIMESTAMP
, TIME
, INTERVAL
).
NOW()
: Возвращает текущую дату и время (в видеtimestamp with time zone
).CURRENT_DATE
: Возвращает текущую дату (в видеdate
).CURRENT_TIME
: Возвращает текущее время (в видеtime with time zone
).-- Выбрать текущую дату и время SELECT NOW(), CURRENT_DATE, CURRENT_TIME;
EXTRACT(field FROM source)
: Извлекает определенную часть (например, год, месяц, день, час, минуту) из значения даты/времени.source
может быть столбцом или значением даты/времени, аfield
может бытьYEAR
,MONTH
,DAY
,HOUR
,MINUTE
,SECOND
,DOW
(день недели, 0=Воскресенье),DOY
(день года) и т.д.-- Извлечь год и месяц приема на работу для сотрудников SELECT hire_date, EXTRACT(YEAR FROM hire_date) AS hire_year, EXTRACT(MONTH FROM hire_date) AS hire_month FROM employees; -- Найти сотрудников, принятых на работу в пятницу (DOW = 5 в PostgreSQL) SELECT first_name, last_name, hire_date FROM employees WHERE EXTRACT(DOW FROM hire_date) = 5;
Примечание:
DATE_PART('field', source)
является PostgreSQL-специфическим эквивалентомEXTRACT
.AGE(timestamp, timestamp)
илиAGE(timestamp)
: Вычисляет интервал между двумя временными метками. Если указан только один аргумент, он вычисляет интервал между аргументом иCURRENT_DATE
. Результат имеет типINTERVAL
, часто отображается в годах, месяцах и днях.-- Вычислить стаж каждого сотрудника на сегодняшний день SELECT first_name, last_name, hire_date, AGE(hire_date) AS tenure FROM employees;
- Арифметика с датами: Вы можете выполнять арифметические операции с использованием
INTERVAL
.-- Найти дату через 90 дней после даты приема на работу SELECT hire_date, hire_date + INTERVAL '90 day' AS review_date FROM employees; -- Найти сотрудников, принятых на работу в течение последних 3 лет SELECT first_name, last_name, hire_date FROM employees WHERE hire_date >= CURRENT_DATE - INTERVAL '3 year';
2.4 Функции преобразования типов
Эти функции преобразуют данные из одного типа в другой.
CAST(expression AS type)
: Стандартный SQL способ выполнения преобразований типов.expression::type
: Упрощенный синтаксис PostgreSQL для приведения типов.-- Преобразовать числовую зарплату в текст для конкатенации SELECT first_name, last_name, 'Salary: ' || CAST(salary AS VARCHAR) AS salary_text FROM employees; -- Использование сокращенной записи PostgreSQL SELECT first_name, last_name, 'Salary: ' || salary::VARCHAR AS salary_text FROM employees; -- Преобразовать текстовую строку даты в тип DATE (если формат стандартный) SELECT CAST('2023-10-26' AS DATE); SELECT '2023-10-26'::DATE;
Внимание: Убедитесь, что данные совместимы с целевым типом, иначе возникнет ошибка.
2.5 Условные выражения (часто используются как функции)
Обеспечивают условную логику внутри запроса.
COALESCE(value1, value2, ...)
: Возвращает первое значение, не равное NULL, в списке аргументов. Очень полезно для предоставления значений по умолчанию для столбцов, содержащих NULL.-- Отображать 'N/A', если столбец middle_name равен NULL SELECT first_name, COALESCE(middle_name, 'N/A') AS middle_name_display, last_name FROM employees; -- Предполагая столбец 'middle_name', допускающий значения NULL
NULLIF(value1, value2)
: Возвращает NULL, еслиvalue1
равноvalue2
, в противном случае возвращаетvalue1
. Полезно для предотвращения ошибок деления на ноль или обработки определенных значений как NULL.-- Демонстрация NULLIF путем возврата NULL, если цена продукта соответствует определенному значению. -- Выберите название продукта, первоначальную цену и цену, измененную с помощью NULLIF. SELECT product_name, price, NULLIF(price, 75.25) AS price_excluding_webcam_value FROM products; -- Демонстрация NULLIF путем возврата NULL, если зарплата сотрудника соответствует определенному значению. SELECT first_name, last_name, salary, NULLIF(salary, 55000.00) AS salary_excluding_charlie_value FROM employees;
CASE WHEN condition1 THEN result1 [WHEN condition2 THEN result2 ...] [ELSE else_result] END
: Стандартный SQL способ реализации логики if-then-else.-- Категоризация продуктов на основе цены SELECT product_name, price, CASE WHEN price < 50.00 THEN 'Budget' WHEN price >= 50.00 AND price < 200.00 THEN 'Mid-Range' ELSE 'Premium' END AS price_category FROM products;
3. Комбинирование функций
Функции можно вкладывать друг в друга или использовать вместе в выражениях.
-- Получить первую инициаль в верхнем регистре для всех сотрудников
SELECT first_name, UPPER(SUBSTRING(first_name FROM 1 FOR 1)) AS initial
FROM employees;
-- Отобразить год и месяц приема на работу, дополненный ведущим нулем, если месяц < 10
SELECT hire_date,
EXTRACT(YEAR FROM hire_date) AS hire_year,
LPAD(CAST(EXTRACT(MONTH FROM hire_date) AS TEXT), 2, '0') AS hire_month_padded -- LPAD добавляет дополнение
FROM employees;
-- Примечание: LPAD - еще одна полезная строковая функция для форматирования.
4. Использование функций в других предложениях
Функции не ограничиваются списком SELECT
. Они могут использоваться в WHERE
, ORDER BY
и иногда в GROUP BY
(однако будьте осторожны, не используйте здесь агрегатные функции).
- В
WHERE
:-- Найти сотрудников, принятых на работу в декабре (Месяц = 12) SELECT first_name, last_name, hire_date FROM employees WHERE EXTRACT(MONTH FROM hire_date) = 12; -- Найти продукты, названия которых (в нижнем регистре) начинаются с 'w' SELECT product_name FROM products WHERE LOWER(product_name) LIKE 'w%';
- В
ORDER BY
:-- Отсортировать сотрудников по длине их фамилии SELECT first_name, last_name FROM employees ORDER BY LENGTH(last_name) DESC; -- Самые длинные фамилии в начале -- Отсортировать продукты по последним 3 символам их названия SELECT product_name FROM products ORDER BY SUBSTRING(product_name FROM LENGTH(product_name) - 2);