Главная страница

Учебнопрактическое пособие москва 2017 удк 004. 655 Ббк 32. 973. 26018. 2 М79 Моргунов, Е. П


Скачать 0.9 Mb.
НазваниеУчебнопрактическое пособие москва 2017 удк 004. 655 Ббк 32. 973. 26018. 2 М79 Моргунов, Е. П
Дата14.11.2022
Размер0.9 Mb.
Формат файлаpdf
Имя файла4_1_1_SQL_Postgres_Bazovy_kurs_2017.pdf
ТипУчебно-практическое пособие
#787182
страница6 из 28
1   2   3   4   5   6   7   8   9   ...   28
SELECT E'PGDAY\n17';
?column?
----------
PGDAY
+
17
(1 строка)
40

При использовании C-стиля необходимо удваивать обратную косую черту, если тре- буется поместить ее в константу буквально. А для включения в содержимое констан- ты символа обратной кавычки можно либо удвоить ее, либо сделать так:
SELECT E'PGDAY\'17';
?column?
----------
PGDAY'17
(1 строка)
Подробно об использовании C-стиля написано в разделе документации 4.1.2.2 «Стро- ковые константы со спецпоследовательностями в стиле C».
4.3 Типы «дата/время»
PostgreSQL поддерживает все типы данных, предусмотренные стандартом SQL для даты и времени. Даты обрабатываются в соответствии с григорианским календарем,
причем, это делается даже в тех случаях, когда дата относится к тому моменту време- ни, когда этот календарь в данной стране еще не был принят. Для этих типов данных предусмотрены определенные форматы для ввода значений и для вывода. Причем,
эти форматы могут не совпадать. Важно помнить, что при вводе значений их нужно заключать в одинарные кавычки, как и текстовые строки.
Начнем рассмотрение с типа date. Рекомендуемый стандартом ISO 8601 формат вво- да дат таков: «yyyy-mm-dd», где символы «y», «m» и «d» обозначают цифру года, ме- сяца и дня соответственно. PostgreSQL позволяет использовать и другие форматы для ввода, например: «Sep 12, 2016», что означает 12 сентября 2016 года. При выво- де значений PostgreSQL использует формат по умолчанию, если не предписан другой формат. По умолчанию используется формат, рекомендуемый стандартом ISO 8601:
«yyyy-mm-dd».
SELECT '2016-09-12'::date;
date
------------
2016-09-12
(1 строка)
А в следующем примере используется другой формат ввода, но формат вывода оста- ется тот же самый, поскольку мы его не изменяли:
SELECT 'Sep 12, 2016'::date;
date
------------
2016-09-12
(1 строка)
41

Чтобы «сказать» СУБД, что введенное значение является датой, а не простой сим- вольной строкой, мы использовали операцию приведения типа. В PostgreSQL она оформляется с использованием двойного символа «двоеточие» и имени того типа, к которому мы приводим данное значение. Важно учесть, что при выполнении приве- дения типа производится проверка значения на соответствие формату целевого типа и множеству его допустимых значений.
В PostgreSQL предусмотрен целый ряд функций для работы с датами и временем. На- пример, для получения значения текущей даты служит функция current_date. Ее осо- бенностью является то, что при ее вызове круглые скобки не используются.
SELECT current_date;
date
------------
2016-09-21
(1 строка)
Если нам требуется вывести дату в другом формате, то для разового преобразования формата можно использовать функцию to_char(), например:
SELECT to_char( current_date, 'mm-dd-yyyy' );
СУБД выведет:
to_char
------------
21-09-2016
(1 строка)
Обратите внимание, что для демонстрации возможностей СУБД по работе с датами нам даже не потребовалось создавать таблицу, а оказалось достаточно лишь команды
SELECT.
Для хранения времени суток служат два типа данных: time и time with time zone.
Первый из них хранит только время суток, а второй — дополнительно — еще и ча- совой пояс. Однако документация на PostgreSQL не рекомендует использовать тип time with time zone, поскольку смещение (offset), соответствующее конкретному ча- совому поясу, может зависеть от даты перехода на летнее время и обратно, но в этом типе дата отсутствует. При вводе значений времени допустимы различные форматы,
например:
SELECT '21:15'::time;
При выводе СУБД дополнит введенное значение, в котором присутствуют только ча- сы и минуты, секундами.
time
----------
21:15:00
(1 строка)
Чтобы «сказать» СУБД, что введенное значение является значением времени, а не простой символьной строкой, мы опять использовали операцию приведения типа.
Предложим СУБД заведомо недопустимое значение времени, например:
42

SELECT '25:15'::time;
Получим такое сообщение об ошибке:
ОШИБКА: значение поля типа date/time вне диапазона: "25:15"
СТРОКА 1: select '25:15'::time;
^
А теперь возьмем значение, которое включает еще и секунды.
SELECT '21:15:26'::time;
time
----------
21:15:26
(1 строка)
А если число секунд недопустимое, то опять получим сообщение об ошибке.
SELECT '21:15:69'::time;
ОШИБКА: значение поля типа date/time вне диапазона: "21:15:69"
СТРОКА 1: select '21:15:69'::time;
^
Время можно вводить не только в 24-часовом формате, но и в 12-часовом, при этом нужно использовать дополнительные суффиксы am и pm. Например:
SELECT '10:15:16 am'::time;
time
----------
10:15:16
(1 строка)
SELECT '10:15:16 pm'::time;
time
----------
22:15:16
(1 строка)
Для получения значения текущего времени служит функция current_time. При ее вы- зове круглые скобки не используются.
SELECT current_time;
timetz
--------------------
23:51:57.293522+03
(1 строка)
43

Текущее время выводится с высокой точностью и дополняется числовым значением,
соответствующим локальному часовому поясу, который установлен в конфигураци- онном файле сервера PostgreSQL. В приведенном примере значение часового пояса равно +03, но если ваш компьютер находится в другом часовом поясе, то это значение будет другим, например, для регионов Сибири оно может быть +08.
В результате объединения типов даты и времени получается интегральный тип —
временная отметка. Этот тип существует в двух вариантах: с учетом часового поя- са — timestamp with time zone, либо без учета часового пояса — timestamp. Для пер- вого варианта существует сокращенное наименование — timestamptz, которое явля- ется расширением PostgreSQL. При вводе и выводе значений этого типа данных ис- пользуются соответствующие форматы ввода и вывода даты и времени. Вот пример с учетом часового пояса:
SELECT timestamp with time zone '2016-09-21 22:25:35';
timestamptz
------------------------
2016-09-21 22:25:35+03
(1 строка)
Обратите внимание, что хотя мы не указали явно значение часового пояса при вводе данных, при выводе это значение «+03» было добавлено.
А это пример без учета часового пояса:
SELECT timestamp '2016-09-21 22:25:35';
timestamp
---------------------
2016-09-21 22:25:35
(1 строка)
В рассмотренных примерах мы использовали синтаксис type 'string' для указания конкретного типа простой литеральной константы. Имя типа данных мы указыва- ли не после преобразуемого литерала, а перед ним, например, timestamp '2016-09-21 22:25:35'. Строго говоря, это не является операцией приведения типа, хотя и похоже на нее. Подробно данный вопрос рассмотрен в разделах документации 4.1.2.7 «Кон- станты других типов» и 4.2.9 «Приведения типов».
Для получения значения текущей временн´ой отметки (т. е. даты и времени в одном значении) служит функция current_timestamp. Она также вызывается без использо- вания круглых скобок. Приведем пример ее использования.
SELECT current_timestamp;
now
-------------------------------
2016-09-27 18:27:37.767739+03
(1 строка)
Здесь в выводе присутствует и часовой пояс: «+03».
Оба типа данных — timestamp и timestamptz — занимают один и тот же объем 8 бай- тов, но значения типа timestamptz хранятся, будучи приведенными к нулевому часо- вому поясу (UTC), а перед выводом приводятся к часовому поясу пользователя.
44

На практике при принятии решения о том, какой из этих двух типов — timestamp или timestamptz — использовать, необходимо учитывать, требуется ли значения, храня- щиеся в таблице, приводить к местному часовому поясу или не требуется. Например,
в расписании авиарейсов указывается местное время как для аэропорта отправле- ния, так и для аэропорта прибытия. Поэтому в таком случае нужно использовать тип timestamp, чтобы это время не приводилось к текущему часовому поясу пользовате- ля, где бы он ни находился.
Из двух этих типов данных чаще используется timestamptz.
Последним типом является interval, который представляет продолжительность от- резка времени между двумя моментами времени. Его формат ввода таков:
quantity unit [quantity unit ...] direction
Здесь unit означает единицу измерения, а quantity — количество таких единиц. В ка- честве единиц измерения можно использовать следующие: microsecond, millisecond,
second, minute, hour, day, week, month, year, decade, century, millennium. Параметр direction может принимать значение ago (т. е. «тому назад») либо быть пустым. При- ведем примеры:
SELECT '1 year 2 months ago'::interval;
interval
------------------
-1 years -2 mons
(1 строка)
Обратите внимание, что параметр ago заставляет СУБД добавить знак «минус» перед всеми полями.
Можно использовать альтернативный формат, предлагаемый стандартом ISO 8601:
P [ years-months-days ] [ T hours:minutes:seconds ]
Здесь строка должна начинаться с символа «P», а символ «T» разделяет дату и время
(все выражение пишется без пробелов). Например:
SELECT 'P0001-02-03T04:05:06'::interval;
interval
-------------------------------
1 year 2 mons 3 days 04:05:06
(1 строка)
Поскольку интервал — это отрезок времени между двумя временн ´ыми отметками, то значение этого типа можно получить при вычитании одной временн´ой отметки из другой.
SELECT ('2016-09-16'::timestamp - '2016-09-01'::timestamp)::interval;
interval
----------
15 days
(1 строка)
45

Как мы уже говорили ранее, в PostgreSQL предусмотрен целый ряд функций для рабо- ты с датами и временем. Например, для получения значений текущей даты, текущего времени и текущей временн´ой отметки (т. е. даты и времени в одном значении) слу- жат функции current_date, current_time, current_timestamp. Эти функции часто при- меняются для получения значений по умолчанию при вставке строк в таблицы. Их особенностью является то, что при их вызове круглые скобки не используются. Для получения полной информации обратитесь к документации (раздел 9.9 «Операторы и функции даты/времени»).
Значения временн ´ых отметок можно усекать с той или иной точностью с помощью функции date_trunc(). Например, для получения текущей временн´ой отметки с точ- ностью до одного часа нужно сделать так:
SELECT ( date_trunc( 'hour', current_timestamp ) );
date_trunc
------------------------
2016-09-27 22:00:00+03
(1 строка)
Из значений временн ´ых отметок можно с помощью функции extract() извлекать от- дельные поля, т. е. год, месяц, день, число часов, минут или секунд и т. д. Например,
чтобы извлечь номер месяца, нужно сделать так:
SELECT extract( 'mon' FROM timestamp '1999-11-27 12:34:56.123459' );
date_part
-----------
11
(1 строка)
Напомним, что выражение timestamp '1999-11-27 12:34:56.123459' не означает опе- рацию приведения типа. Оно присваивает тип данных timestamp литеральной кон- станте.
4.4 Логический тип
Логический (boolean) тип может иметь три состояния: «true» и «false», а также неопре- деленное состояние, которое можно представить значением NULL. Таким образом,
тип boolean реализует трехзначную логику.
В качестве состояния «true» могут служить следующие значения: TRUE, 't', 'true', 'y',
'yes', 'on', '1'.
В качестве состояния «false» могут служить следующие значения: FALSE, 'f', 'false', 'n',
'no', 'off', '0'.
В качестве примера создадим таблицу с двумя столбцами и добавим в нее несколько строк:
CREATE TABLE databases ( is_open_source boolean, dbms_name text );
46

INSERT INTO databases VALUES ( TRUE, 'PostgreSQL' );
INSERT INTO databases VALUES ( FALSE, 'Oracle' );
INSERT INTO databases VALUES ( TRUE, 'MySQL' );
INSERT INTO databases VALUES ( FALSE, 'MS SQL Server' );
Теперь выполним выборку всех строк из этой таблицы:
SELECT * FROM databases;
is_open_source | dbms_name
----------------+--------------- t
| PostgreSQL
f
| Oracle t
| MySQL
f
| MS SQL Server
(4 строки)
Выберем только СУБД с открытым исходным кодом:
SELECT * FROM databases WHERE is_open_source;
is_open_source | dbms_name
----------------+------------ t
| PostgreSQL
t
| MySQL
(2 строки)
Обратите внимание, что в условии WHERE для проверки логических значений можно не писать выражение WHERE is_open_source = 'yes', а достаточно просто указать имя столбца, содержащего логическое значение: WHERE is_open_source.
4.5 Массивы
PostgreSQL позволяет создавать в таблицах такие столбцы, в которых будут содер- жаться не скалярные значения, а массивы переменной длины. Эти массивы могут быть многомерными и могут содержать значения любого из встроенных типов, а так- же типов данных, определенных пользователем.
Предположим, что нам необходимо сформировать и сохранить в базе данных в удоб- ной форме графики работы пилотов авиакомпании, т. е. номера дней недели, когда они совершают полеты. Создадим таблицу, в которой эти графики будут храниться в виде единых списков, т. е. в виде одномерных массивов.
CREATE TABLE pilots
( pilot_name text,
schedule integer[]
);
CREATE TABLE
47

Для указания на то, что это массив, нужно добавить квадратные скобки к наимено- ванию типа данных. При этом задавать число элементов не обязательно.
Давайте добавим в таблицу четыре строки. Массив в команде вставки представлен в виде строкового литерала с указанием типа данных и квадратных скобок, означаю- щих массив. Обратите внимание, что все массивы имеют различное число элемен- тов.
INSERT INTO pilots
VALUES ( 'Ivan', '{ 1, 3, 5, 6, 7 }'::integer[] ),
( 'Petr', '{ 1, 2, 5, 7
}'::integer[] ),
( 'Pavel', '{ 2, 5
}'::integer[] ),
( 'Boris', '{ 3, 5, 6
}'::integer[] );
INSERT 0 4
Посмотрим, что получилось:
SELECT * FROM pilots;
pilot_name | schedule
------------+-------------
Ivan
| {1,3,5,6,7}
Petr
| {1,2,5,7}
Pavel
| {2,5}
Boris
| {3,5,6}
(4 строки)
Предположим, что руководство компании решило, что каждый пилот должен летать 4
раза в неделю. Значит, нам придется обновить значения в таблице. Пилоту по имени
Boris добавим один день с помощью операции конкатенации:
UPDATE pilots
SET schedule = schedule || 7
WHERE pilot_name = 'Boris';
UPDATE 1
Пилоту по имени Pavel добавим один день в конец списка (массива) с помощью функ- ции array_append:
UPDATE pilots
SET schedule = array_append( schedule, 6 )
WHERE pilot_name = 'Pavel';
UPDATE 1
Ему же добавим один день в начало списка с помощью функции array_ prepend (об- ратите внимание, что параметры функции поменялись местами):
UPDATE pilots
SET schedule = array_prepend( 1, schedule )
WHERE pilot_name = 'Pavel';
UPDATE 1 48

У пилота по имени Ivan имеется лишний день в графике. С помощью функции array_remove удалим из графика пятницу (второй параметр функции указывает зна-
чение
элемента массива, а не индекс):
UPDATE pilots
SET schedule = array_remove( schedule, 5 )
WHERE pilot_name = 'Ivan';
UPDATE 1
У пилота по имени Petr изменим дни полетов, не изменяя их общего количества. Вос- пользуемся индексами для работы на уровне отдельных элементов массива. По умол- чанию нумерация индексов начинается с единицы, а не с нуля. При необходимости ее можно изменить. К элементам одного и того же массива можно обращаться в пред- ложении SET по отдельности, как будто это разные столбцы.
UPDATE pilots
SET schedule[ 1 ] = 2, schedule[ 2 ] = 3
WHERE pilot_name = 'Petr';
UPDATE 1
А можно было бы, используя срез (slice) массива, сделать и так:
UPDATE pilots
SET schedule[ 1:2 ] = ARRAY[ 2, 3 ]
WHERE pilot_name = 'Petr';
UPDATE 1
В вышеприведенной команде запись 1:2 означает индексы первого и последнего эле- ментов диапазона массива. Нотация с использованием ключевого слова ARRAY — это альтернативный способ создания массива (он соответствует стандарту SQL). Таким образом, присваивание новых значений производится сразу целому диапазону эле- ментов массива.
SELECT * FROM pilots;
pilot_name | schedule
------------+-----------
Boris
| {3,5,6,7}
Pavel
| {1,2,5,6}
Ivan
| {1,3,6,7}
Petr
| {2,3,5,7}
(4 строки)
Теперь продемонстрируем основные операции, которые можно применять к масси- вам, выполняя выборки из таблиц.
Получим список пилотов, которые летают каждую среду:
SELECT * FROM pilots
WHERE array_position( schedule, 3 ) IS NOT NULL;
49
pilot_name | schedule
------------+-----------
Boris
| {3,5,6,7}
Ivan
| {1,3,6,7}
Petr
| {2,3,5,7}
(3 строки)
Функция array_position возвращает индекс первого вхождения элемента с указанным значением в массив. Если же такого элемента нет, она возвратит NULL.
Выберем пилотов, летающих по понедельникам и воскресеньям:
SELECT *
FROM pilots
WHERE schedule @> '{ 1, 7 }'::integer[];
pilot_name | schedule
------------+-----------
Ivan
| {1,3,6,7}
(1 строка)
Оператор @> означает проверку того факта, что в левом массиве содержатся все эле- менты правого массива. Конечно, при этом в левом массиве могут находиться и дру- гие элементы, что мы и видим в графике этого пилота.
Еще аналогичный вопрос: кто летает по вторникам и/или по пятницам? Для получе- ния ответа воспользуемся оператором &&, который проверяет наличие общих эле- ментов у массивов, т. е. пересекаются ли их множества значений. В нашем примере число общих элементов, если они есть, может быть равно одному или двум. Здесь мы также использовали нотацию с ключевым словом ARRAY, а не '{ 2, 5 }'::integer[]. Вы можете применять ту, которая принята в рамках выполнения вашего проекта.
SELECT *
FROM pilots
WHERE schedule && ARRAY[ 2, 5 ];
pilot_name | schedule
------------+-----------
Boris
| {3,5,6,7}
Pavel
| {1,2,5,6}
Petr
| {2,3,5,7}
(3 строки)
Сформулируем вопрос в форме отрицания: кто не летает ни во вторник, ни в пятни- цу? Для получения ответа добавим в предыдущую SQL-команду отрицание NOT:
1   2   3   4   5   6   7   8   9   ...   28


написать администратору сайта