Объектно-реляционная СУБД PostgreSQL. Учебное пособие
.pdfФункциональные индексы
Если в запросах часто используется какая-то функция, то выгодно создать функциональный индекс. Приведём пример. Предположим, что мы хотим найти описание некоторого термина в словаре. Сам термин может быть написан по-разному, например с прописной или строчной буквы. В этом случае в запросе приходится перебирать все предполагаемые варианты написания этого термина, например:
SELECT Термин, Описание FROM Словарь WHERE Термин = 'База' OR Термин = 'база';
Для упрощения запроса преобразуем искомое слово к нижнему регистру:
SELECT Термин, Описание FROM Словарь WHERE lover(Термин) = 'база';
Функция lover заменяет прописные буквы строчными. В этом случае для ускорения поиска можно создать функциональный индекс
CREATE INDEX idx_word ON Словарь(lover(Термин));
Другой пример. Пусть в таблице Поэты имя и фамилия человека хранятся в отдельных колонках: first_name и last_name. Запрос вида
SELECT * FROM Поэты WHERE (first_name||' '|| last_name) = 'Владимир Маяковский';
будет выполняться быстрее, если создать индекс
CREATE INDEX idx_name
ON Поэты(first_name||' '||last_name);
Заметим, что функциональный индекс при операциях вставки и обновления записей требует большего времени на изменение, поэтому функциональные индексы рекомендуется использовать, если операции поиска превалируют над остальными операциями в базе данных.
Частичные индексы
Частичный индекс – это индекс, построенный на части таблицы, удовлетворяющей определенному условию. В этом случае размер индекса
91
пропорционален просматриваемой части таблицы, следовательно, может быть значительно меньше полного индекса.
Пример. Пусть в БД «Библиотека» хранится информация о взятых читателями книгах. Пусть в таблице
ВыдачаКниг (КодЧитателя, КодКниги, ДатаВозврата, Отметка)
поле Отметка содержит значение true, если книга сдана, или значение false, если книга не сдана. Понятно, что если база данных ведётся длительное время, то большинство записей содержит значение отметки true, и просматривать эти записи в большинстве случаев нет смысла (можно предположить, что большинство запросов будет касаться читателей, ещё не сдавших ту или иную книгу).
Поэтому создадим частичный индекс:
CREATE INDEX idx_невозврат ON ВыдачаКниг(Отметка) WHERE Отметка = false;
Тогда запрос "какие книги не сдал этот читатель?"
SELECT * FROM ВыдачаКниг
WHERE КодЧитателя = 296 AND Отметка = false;
будет использовать указанный индекс, а запрос "кто брал эту книгу?"
SELECT * FROM ВыдачаКниг
WHERE КодКниги = 256134;
– нет.
Битовые индексы
Битовый индекс используется, если индексируемый столбец данных содержит всего несколько различных значений. В этой ситуации использование битового индекса существенно ускоряет выборку значений по такому столбцу. Типичный пример – пол человека, имеющий всего два различных значения.
Рассмотрим таблицу Люди, содержащую идентификатор id, имя, фамилию и пол человека. Пусть она содержит следующий набор записей:
id |
Имя |
Фамилия |
Пол |
1 |
Елена |
Иванова |
жен |
2 |
Сергей |
Петров |
муж |
92
3 |
Виктор |
Панин |
муж |
4 |
Виктория |
Панова |
жен |
5 |
Николай |
Кузнецов |
муж |
Битовый индекс по полю Пол формирует так называемую битовую карту:
Значение |
Начало |
Конец |
Маска |
|
атрибута |
||||
|
|
|
||
'муж' |
1 |
5 |
01101 |
|
'жен' |
1 |
5 |
10010 |
Каждый разряд маски занимает один бит. В силу ограниченности длины маски набор записей разбивается на группы, Начало и Конец – это номера первой и последней записи в группе. В данном примере группу образуют все записи примера.
Битовый индекс позволяет ускорить поиск записей с заданным значением атрибута Пол. Например, при поиске женщин будет просматриваться маска 10010, в которой единицы соответствуют записям с id = 1 (Елена Иванова) и id = 4 (Виктория Панова).
Понятно, что если разных значений в индексируемом столбце будет больше, то будет больше и строк в битовом индексе.
5.Специальные возможности
5.1.Работа с текстом
1.Ввод данных из текстового файла
Втом случае, когда данные, которые необходимо разместить в базе данных, представлены в виде текстового файла, который не является sql-файлом, возникает необходимость считать эти данные и «разложить» их по таблицам БД.
Для работы с файлами PostgreSQL содержит команды COPY… FROM…
(чтение из файла) и COPY… TO… (запись в файл). Простейшие варианты команд содержат только обязательные элементы.
COPY ИмяТаблицы FROM 'путь\имя_файла'; копирует данные из файла в таблицу. По умолчанию файл является текстовым, но можно использовать и
93
файлы с расширением csv (Comma Separated Values), в котором значения атрибутов таблицы разделяются запятыми (такие файлы легко можно получить из MS Excel) и двоичные (типизированные) файлы.
COPY {ИмяТаблицы | Запрос} TO 'путь\имя_файла'; – копирует данные из таблицы или результат выполнения запроса в файл. По умолчанию данные выводятся в текстовый файл, значения атрибутов разделяются символом табуляции, но программист может задать свой символ-разделитель (пробел в качестве разделителя использовать не рекомендуется, так как если в таблице есть значения-строки, то они могут содержать пробелы). При выводе в файл формата csv данные записи разделяются запятыми (но для этого в конец оператора следует добавить опцию WITH (FORMAT csv) или WITH (DELIMITER ',')); при выводе в бинарный (типизированный) файл разделители не используются.
Заметим, что при выводе данных в файл необходимо учитывать кодировку символов, так как файл может использоваться в приложении, которое работает в другой кодировке. Так, для использования в MS Excel при выводе в файл следует задать опцию ENCODING, например, так:
WITH (FORMAT csv, ENCODING win1251)
2. Функции для работы со строками
PostgreSQL содержит достаточно большой набор возможностей для работы со строками. Заметим сразу, что переменная типа text с точки зрения PostgreSQL не является массивом символов, поэтому конструкция строка[индекс] вызывает сообщение об ошибке.
–Конкатенация (слияние) строк (строки и числа): строка1 || строка2; строка || число; число || строка; Результат операции всегда является строкой.
–Выделение части строки.
94
Функция substring(строка from позиция for количество)
выделяет подстроку, которая начинается с заданной позиции строки и содержит заданное количество символов строки.
Пример.
line = 'текстовая строка';
part = substring(line from 1 for 5);
В результате переменная part будет содержать слово 'текст'.
Функция substring(строка from шаблон) выделяет подстроку, соответствующую заданному шаблону (регулярному выражению).
Функция split_part(строка, разделитель, номер) делит строку на части, используя символ-разделитель, возвращает часть с заданным номером (нумерация частей начинается с 1).
Примеры.
line := 'текстовая строка';
part := split_part(line, ' ', 2);
В результате переменная part будет содержать слово 'строка'.
line := '16:07:55';
part := split_part(line, ':', 1);
В результате переменная part будет содержать число 16.
Заметим, что в приведенных примерах переменная part должна объявляться с учётом типа значения, возвращаемого функцией split_part(…).
– Замена подстроки в строке.
Замена подстроки реализуется с помощью функции
overlay(строка placing подстрока from позиция for количество)
Параметры функции: строка – исходная строка;
подстрока – используется для замены части исходной строки;
from позиция for количество – определяет, какая часть исходной строки будет заменена.
95
Пример.
overlay('ручка' placing 'баш' from 3 for 1);
Подстрока 'баш' заменит в слове 'ручка' подстроку, состоящую из одного символа, и начинающуюся в позиции 3 (т.е., символ 'ч'). В результате получится 'ру'+'баш'+'ка', т.е., слово 'рубашка'.
Эту функцию можно использовать не только для замены, но и для удаления или вставки подстроки.
Приведём несколько примеров.
Удаление: заменим символ 'ч' пустой строкой.
overlay('ручка' placing '' from 3 for 1);
Функция вернёт слово 'рука'.
Вставка: заменим ноль символов строкой 'он'.
overlay('ручка' placing 'он' from 4 for 0);
Функция вернёт слово 'ручонка'.
Для замены подстроки в строке можно также воспользоваться функцией
replace(строка, подстрока, заменитель),
которая заменит все вхождения указанной подстроки в исходной строке на заменитель.
Пример.
S = 'гав-гав'; replace(S, 'гав', 'мяу');
Теперь в переменной S содержится текст 'мяу-мяу'.
– Разные функции
trim([параметр] [символ] from строка) удаляет максимальное количество повторений заданного символа (по умолчанию – пробела) с концов строки. Значения параметра:
leading – удаляются начальные символы строки; trailing – удаляются конечные символы строки;
both – удаляются и начальные и конечные символы строки. length(строка) возвращает количество символов в строке
96
repeat(строка, число) возвращает строку, повторенную заданное число раз.
left(строка, N) возвращает N первых символов строки, при отрицательном N возвращает строку без |N| последних символов.
right(строка, N) возвращает N последних символов строки, при отрицательном N возвращает строку без |N| первых символов.
5.2. Регулярные выражения
Регулярные выражения широко используются в задачах поиска, проверки соответствия заданному шаблону. PostgreSQL содержит 4 функции, которые обеспечивают проверку соответствия, замену подстрок и разделение строки на части:
regexp_matches(строка, шаблон [, флаги]) – по умолчанию возвращает первое вхождение подстроки, соответствующей заданному шаблону. Если установлен флаг 'g' (от global), то возвращаются все вхождения.
regexp_replace(строка, шаблон, заменитель [, флаги]) по умолчанию заменяет первое вхождение подстроки, соответствующей шаблону, на строку-заменитель. Если установлен флаг 'g', то заменяются все вхождения.
regexp_split_to_array(строка, шаблон [, флаги]) – делит строку на части, используя шаблон в качестве разделителя, и возвращает набор частей в виде массива строк.
regexp_split_to_table(строка, шаблон [, флаги]) –
работает аналогично предыдущей функции, но возвращает таблицу.
Регулярное выражение представляет собой текстовую строку, содержащую как обычные, так и специальные символы («метасимволы»), которые трактуются как операторы, задающие правила поиска. Перечислим основные метасимволы:
. (точка) – любой символ; [символы] – один из указанных внутри квадратных скобок символов
(диапазон подряд идущих символов можно указывать через дефис); [^символы] – любой кроме указанных символов;
97
\s – пробел;
\S – непробельный символ;
\d – цифра (соответствует шаблону [0-9])
\D – не цифра (соответствует шаблону [^0-9]) \w – буква или цифра;
\W – не буква и не цифра;
* – ноль или больше повторений предыдущего символа или группы; + – одно или больше повторений предыдущего символа или группы; ? – ноль или одно повторение предыдущего символа или группы; | – логический оператор ИЛИ; ! – логическое отрицание – оператор НЕ;
{m} – ровно m повторений предыдущего элемента;
{m,} – ровно m или более повторений предыдущего элемента; {m, n} – от m до n (включительно) повторений;
^ – действует на начало строки; $ – действует на конец строки; Примеры:
[0-9]+ – последовательность из одной или нескольких цифр; ^[1-9][0-9]+ – натуральное число (первая цифра не 0); [A-Za-z] – любой символ латиницы;
[A-Z]{3} – слово из трёх прописных символов латиницы.
Если в подстроке, которую мы ищем, встречаются служебные символы, то в шаблоне перед ними следует поставить символ "\" – обратный слеш.
Примеры:
^[1-9][0-9]+\.[0-9]+ | 0\.[0-9]+ – вещественное число без знака;
\.[a-z]+$ – расширение файла (с разделяющей точкой).
Круглые скобки выделяют группу символов, их наличие или отсутствие меняет возвращаемое значение. Приведём примеры.
98
SELECT regexp_matches('Число 35617','\d{4}');
Результат – строка '3561' – четыре подряд идущих цифры. Теперь добавим в шаблон скобки:
SELECT regexp_matches('Число 35617','(\d){4}');
Результат – символ '1' – четвёртая цифра в строке поиска.
SELECT regexp_matches('123456789','(\d){2}', 'g');
Результат – извлекается каждая вторая цифра из каждой группы цифр. Каждая выделенная скобками группа символов автоматически получает
номер, который можно использовать в этом же шаблоне. Пусть, например, мы хотим выявить все палиндромы из пяти символов в строке. Тогда требуемый шаблон будет таким: '(\w)(\w)\w\2\1'. В этом шаблоне заложен следующий порядок символов: первый символ – это группа номер 1, второй символ – группа номер 2, затем идёт третий, за ним второй и, наконец, первый символ. Эти символы образуют палиндром, который может быть частью другого слова. Поэтому запрос
SELECT regexp_matches('минимум', '(\w)(\w)\w\2\1');
выявит наличие палиндрома "миним" в слове "минимум".
Здесь приведена лишь небольшая часть возможностей регулярных выражений, за дополнительной информацией читатель может обратиться к документации по СУБД PostgreSQL.
5.3. Полнотекстовый поиск
Операторы LIKE и ILIKE поиска по текстовым данных в запросах PostgreSQL дают удовлетворительные результаты только при ограниченном размере текста. Они имеют слабую лингвистическую поддержку, требуют много времени для поиска в больших текстовых документах. Использование регулярных выражений также не всегда эффективно решает задачу поиска в текстовых документах. В этих случаях разработчики СУБД PostgreSQL предлагают использовать совершенно другой подход для поиска информации в большом массиве документов – полнотекстовый поиск. Идея полнотекстового
99
поиска состоит в том, чтобы при включении документа в базу данных разделить его на лексемы и запомнить их положение в документе. При выделении лексем используются специальные программы-словари для нормализации слов, чтобы уменьшить количество уникальных лексем.
Для разделения документа на лексемы в СУБД PostgreSQL используется функция to_tsvector([конфигурация,] документ), которая возвращает значение типа tsvector – набор лексем документа (ts – это сокращение от text search).
Для формирования набора лексем, по которым будет осуществляться поиск, используется функция to_tsquery([конфигурация,] строка поиска). Параметр «конфигурация» в указанных функциях определяет язык и содержит дополнительную информацию, необходимую для выделения лексем. Если параметр опущен, то используется конфинурация, заданная в файле настройки СУБД PostgreSQL.
Оператор сравнения документа и запроса записывается с помощью символов @@.
Примеры.
А) Запрос
SELECT to_tsvector('Информационные технологии в
технологиях профессиональной деятельности');
вернёт в качестве результата набор пар 'лексема' : список, где «список» есть номера слов документа, родственных выделенной лексеме. В данном примере запрос вернёт вектор-строку, содержащую следующую информацию:
'деятельн' : 6
'информацион' : 1 'профессиональн' : 5 'технолог' : 2,4
Замечания:
1) вспомогательные слова (предлоги, союзы и др.) не входят в результирующий набор, в данном примере это предлог «в», и его нет в результирующем наборе;
100
