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

Учебнопрактическое пособие москва 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
страница9 из 28
1   ...   5   6   7   8   9   10   11   12   ...   28
SELECT * FROM pilots;
pilot_name | schedule
|
meal
------------+-------------+-------------------------
Ivan
| {1,3,5,6,7} | {сосиска,макароны,кофе}
Petr
| {1,2,5,7}
| {котлета,каша,кофе}
Pavel
| {2,5}
| {сосиска,каша,кофе}
Boris
| {3,5,6}
| {котлета,каша,чай}
(4 строки)
Давайте получим список пилотов, предпочитающих на обед сосиски:
SELECT * FROM pilots WHERE meal[ 1 ] = 'сосиска';
68
pilot_name |
schedule |
meal
------------+-------------+-------------------------
Ivan
| {1,3,5,6,7} | {сосиска,макароны,кофе}
Pavel
| {2,5}
| {сосиска,каша,кофе}
(2 строки)
Предположим, что руководство авиакомпании решило, что пища пилотов долж- на быть разнообразной. Оно позволило им выбрать свой рацион на каждый из четырех дней недели, в которые пилоты совершают полеты. Для нас это ре- шение руководства выливается в необходимость модифицировать таблицу, а именно: столбец meal теперь будет содержать двумерные массивы. Определе- ние этого столбца станет таким:
meal text[][]
Задание.
Создайте новую версию таблицы и соответственно измените коман- ду INSERT, чтобы в ней содержались литералы двумерных массивов. Они будут выглядеть примерно так:
'{ { "сосиска", "макароны", "кофе" },
{ "котлета", "каша", "кофе" },
{ "сосиска", "каша", "кофе" },
{ "котлета", "каша", "чай" } }'::text[][]
Сделайте ряд выборок и обновлений строк в этой таблице. Для обращения к эле- ментам двухмерного массива нужно использовать два индекса. Не забывайте,
что по умолчанию номера индексов начинаются с единицы.
34. В тексте раздела 4.6 мы выполняли обновление JSON-объекта с помощью функ- ции jsonb_set: добавляли значение в массив. Для обновления скалярных значе- ний, например, по ключу trips, можно сделать так:
UPDATE pilot_hobbies
SET hobbies = jsonb_set( hobbies, '{ trips }', '10' )
WHERE pilot_name = 'Pavel';
UPDATE 1
Второй параметр функции — это путь в пределах JSON-объекта. Он теперь пред- ставляет собой лишь имя ключа. Однако его необходимо заключить в фигурные скобки. Третий параметр — это новое значение. Хотя оно числовое, но все равно требуется записать его в одинарных кавычках.
SELECT pilot_name, hobbies->'trips' AS trips FROM pilot_hobbies;
pilot_name | trips
------------+-------
Ivan
| 3
Petr
| 2
Boris
| 0
Pavel
| 10
(4 строки)
Задание.
Самостоятельно выполните изменение значения по ключу home_lib в одной из строк таблицы.
69

35. Изучая приемы работы с типами JSON, можно, как и в случае с массивами, поль- зоваться способностью команды SELECT обходиться без создания таблиц. Пока- жем лишь один пример. Для добавления нового ключа и соответствующего ему значения в уже существующий объект, можно воспользоваться оператором «| |»:
SELECT '{ "sports": "хоккей" }'::jsonb || '{ "trips": 5 }'::jsonb;
?column?
----------------------------------
{"trips": 5, "sports": "хоккей"}
(1 строка)
Для работы с типами JSON предусмотрено много различных функций и опе- раторов, представленных в разделе документации 9.15 «Функции и операторы
JSON». Самостоятельно ознакомьтесь с ними, используя описанную технологию работы с командой SELECT.
36.* Объекты JSON в разных строках таблицы могут иметь различные наборы клю- чей. Добавьте дополнительный ключ и соответствующее ему значение в JSON- объект какой-нибудь строки таблицы pilots. Воспользуйтесь оператором «| |».
37. Объекты JSON позволяют не только добавлять в них новые ключи, но также и удалять из них ключи существующие. Удалите один из ключей из JSON-объекта какой-нибудь строки таблицы pilots. Соответствующее ему значение будет так- же удалено, т. к. без ключа оно не может существовать. Воспользуйтесь опера- тором «−».
70

5 Основы языка определения данных
Как мы уже говорили ранее, язык SQL традиционно разделяется на две группы команд. Пер- вая из них предназначена для определения данных, т. е. для создания объектов базы данных,
таких, например, как таблицы. Вторая группа команд служит для выполнения различных опе- раций с данными, таких, как вставка строк в таблицы, выполнение запросов к ним, обновление и удаление строк из таблиц. В этой главе мы сосредоточимся на командах первой группы, т. е.
на определении данных. Рассмотрим все таблицы базы данных «Авиаперевозки».
5.1 Значения по умолчанию и ограничения целостности
В последующих параграфах этой главы в качестве «опорной» базы данных мы будем использовать базу данных «Авиаперевозки», описанную в первой главе. Однако ос- новные сведения о значениях по умолчанию и ограничениях мы проиллюстрируем на той простой базе данных, состоящей из двух таблиц — «Студенты» и «Успевае- мость», о которой речь шла также в первой главе пособия.
Сначала представим описание таблицы «Студенты» (students). Она имеет следующую структуру (т. е. набор атрибутов и их типы данных):
Описание атрибута
Имя атрибута
Тип данных
Тип PostgreSQL
Ограничения
№ зачетной книжки record_book
Числовой numeric(5)
NOT NULL
Ф. И. О.
name
Символьный text
NOT NULL
Серия документа doc_ser
Числовой numeric(4)
Номер документа doc_num
Числовой numeric(6)
Для атрибута «Серия документа, удостоверяющего личность» мы выбрали числовой тип, хотя, пожалуй, более дальновидным был бы выбор символьного типа (см. зада- ние 10 в конце главы).
Теперь перейдем к таблице «Успеваемость» (progress). Структура ее такова:
Описание атрибута
Имя атрибута
Тип данных
Тип PostgreSQL
Ограничения
№ зачетной книжки record_book
Числовой numeric(5)
NOT NULL
Учебная дисциплина subject
Символьный text
NOT NULL
Учебный год acad_year
Символьный text
NOT NULL
Семестр term
Числовой numeric(1)
NOT NULL
term = 1 OR term = 2
Оценка mark
Числовой numeric(1)
DEFAULT 5
mark >= 3 AND
mark <= 5
В описаниях таблиц «Студенты» и «Успеваемость» есть колонки «Ограничения». Они уже содержат конкретные сведения, хотя ограничения мы еще детально не рассмат- ривали. Таким образом, мы немного забежали вперед, но по мере рассмотрения огра- ничений вам будет становиться понятно назначение каждого из них в обеих табли- цах.
71

Итак, начнем со значений по умолчанию. При работе с базами данных нередко воз- никают ситуации, когда то или иное значение является типичным для какого-то кон- кретного столбца. Например, если мы при проектировании таблицы «Успеваемость»
(progress), предназначенной для учета успеваемости студентов, знаем, что их успехи,
как правило, заслуживают оценки «отлично», то в команде CREATE TABLE мы можем отразить этот факт с помощью ключевого слова DEFAULT:
CREATE TABLE progress
...
mark numeric( 1 ) DEFAULT 5,
...
Теперь перейдем к рассмотрению различных видов ограничений (constraints). Будем придерживаться того же порядка, в котором они представлены в документации.
Первым будет ограничение CHECK. Для его создания используется ключевое сло- во CHECK, за которым следует выражение в круглых скобках, содержащее одно или несколько условий, налагаемых на атрибуты таблицы. Это ограничение бывает двух видов: ограничение уровня атрибута и уровня таблицы. Различие между ними только в синтаксическом оформлении: в обоих случаях в выражении могут содержаться об- ращения не только к одному, но также и к нескольким атрибутам таблицы. В первом случае ограничение CHECK является частью определения одного конкретного атри- бута, а во втором случае оно записывается как самостоятельный элемент определе- ния таблицы. Каждое ограничение имеет имя. Мы можем задать его сами с помощью ключевого слова CONSTRAINT. Если же мы этого не сделаем, тогда СУБД сформирует имя автоматически. Когда мы задаем имя сами, мы можем выбрать его с учетом сути налагаемых ограничений, с позиции предметной области. Если же это имя форми- рует СУБД, оно будет сформировано «механически», т. к. СУБД не знает ни сути этих ограничений, ни специфики предметной области.
В качестве примера приведем ограничения, налагаемые на атрибуты term и mark из таблицы «Успеваемость» (progress). Семестр может иметь только два значения: 1 и 2.
Отметка фактически может иметь только три значения: 3, 4 или 5.
CREATE TABLE progress
( ...
term numeric( 1 ) CHECK ( term = 1 OR term = 2 ),
mark numeric( 1 ) CHECK ( mark >= 3 AND mark <= 5 ),
...
);
В данном случае можно и не давать этим ограничениям какие-либо специфические имена, поскольку суть этих ограничений очевидна. Тем не менее, поскольку имена ограничений используются в тех сообщениях, которые выводит СУБД при попытке нарушения ограничений, все же можно придумать для них осмысленные имена, ко- торые облегчат понимание причин появления сообщений об ошибках.
В качестве примера приведем ограничение на допустимые значения атрибута mark,
а оформим его как ограничение уровня таблицы:
CREATE TABLE progress
( ...
mark numeric( 1 ),
CONSTRAINT valid_mark CHECK ( mark >= 3 AND mark <= 5 ),
72

...
);
Следующим видом ограничений, который мы рассмотрим, будет NOT NULL. Оно означает, что в столбце таблицы, на который наложено это ограничение, должны обязательно присутствовать какие-либо определенные значения. При разработке баз данных, исходя из логики конкретной предметной области, зачастую требуется ис- пользовать это ограничение. Как сказано в документации, оно функционально экви- валентно ограничению CHECK ( column_name IS NOT NULL ), но в PostgreSQL создание явного ограничения NOT NULL является более эффективным подходом.
Еще один вид ограничений — это ограничение уникальности UNIQUE. Такое огра- ничение, наложенное на конкретный столбец, означает, что все значения, содержа- щиеся в этом столбце в различных строках таблицы, должны быть уникальными,
т. е. не должны повторяться. Ограничение уникальности может включать в себя и несколько столбцов. В этом случае уникальной должна быть уже комбинация их зна- чений.
Когда в ограничение уникальности включается только один столбец, то можно задать ограничение непосредственно в определении столбца. Например, для таблицы «Сту- денты» (students) было бы логично потребовать, чтобы уникальными были значения столбца record_book:
CREATE TABLE students
( record_book numeric( 5 ) UNIQUE,
...
Это ограничение можно было бы записать и так, дав ему осмысленное имя:
CREATE TABLE students
( record_book numeric( 5 ),
name text NOT NULL,
...
CONSTRAINT unique_record_book UNIQUE ( record_book ),
...
);
Опять обратимся к таблице «Студенты» (students) и покажем, как можно создать огра- ничение уникальности, включающее более одного столбца. В этой таблице первич- ным ключом является столбец record_book, но очевидно, что комбинация значений серии и номера документа, удостоверяющего личность, является уникальной. Поэто- му можно модифицировать определение таблицы таким образом:
CREATE TABLE students
( ...
doc_ser numeric( 4 ),
doc_num numeric( 6 ),
...
CONSTRAINT unique_passport UNIQUE ( doc_ser, doc_num ),
...
);
73

При добавлении ограничения уникальности автоматически создается индекс на ос- нове B-дерева для поддержки этого ограничения.
Переходим к первичным ключам. Как мы уже говорили ранее, этот ключ является уникальным идентификатором строк в таблице. Ключ может быть как простым, т. е.
включать только один атрибут, так и составным, т. е. включать более одного атри- бута. При этом в отличие от уникального ключа, определяемого с помощью ограни- чения UNIQUE, атрибуты, входящие в состав первичного ключа, не могут иметь зна- чений NULL. Таким образом, определение первичного ключа эквивалентно опреде- лению уникального ключа, дополненного ограничением NOT NULL. Однако не стоит в реальной работе заменять первичный ключ комбинацией ограничений UNIQUE и
NOT NULL, поскольку теория баз данных требует наличия в каждой таблице именно первичного ключа. Первичный ключ является частью метаданных, его наличие поз- воляет другим таблицам использовать его в качестве уникального идентификатора строк в данной таблице. Это удобно, например, при создании внешних ключей, речь о которых пойдет ниже. Перечисленными свойствами обладает также и уникальный ключ.
Если первичный ключ состоит из одного атрибута, то можно указать его непосред- ственно в определении этого атрибута:
CREATE TABLE students
( record_book numeric( 5 ) PRIMARY KEY,
...
);
А можно сделать это и в виде отдельного ограничения:
CREATE TABLE students
( record_book numeric( 5 ),
...
PRIMARY KEY ( record_book )
);
В случае создания составного первичного ключа имена столбцов, входящих в его со- став, перечисляются в выражении PRIMARY KEY через запятую:
PRIMARY KEY ( column1, column2, ...)
При добавлении первичного ключа автоматически создается индекс на основе B- дерева для поддержки этого ограничения.
В таблице может быть любое число ограничений UNIQUE, дополненных ограничени- ем NOT NULL, но первичный ключ может быть только один. PostgreSQL допускает и отсутствие первичного ключа, хотя строгая теория реляционных баз данных не реко- мендует так поступать.
Завершаем наш обзор различных видов ограничений рассмотрением такого важно- го понятия, как внешний ключ (foreign key). Внешние ключи являются средством поддержания так называемой ссылочной целостности (referential integrity) между связанными таблицами. Напомним, что это означает, на примере таблиц «Студенты»
(students) и «Успеваемость» (progress). В первой из них содержатся данные о студен- тах, а во второй — сведения об их успеваемости. Поскольку в процессе обучения сту- денты сдают целый ряд зачетов и экзаменов, то в таблице «Успеваемость» для каждо- го студента может присутствовать несколько строк. Для большинства из них это так
74
и будет, хотя, в принципе, возможна ситуация, когда для какого-то студента в таб- лице «Успеваемость» не окажется ни одной строки (если, он, например, находится в академическом отпуске).
Конечно, должна быть возможность определить, какому студенту принадлежат те или иные оценки, т. е. какие строки в таблице «Успеваемость» с какими строками в таблице «Студенты» связаны. Для решения этой задачи не требуется в каждой строке таблицы «Успеваемость» повторять все сведения о студенте: номер зачетной книж- ки, фамилию, имя и отчество, данные документа, удостоверяющего личность. Доста- точно включить в состав каждой строки таблицы «Успеваемость» лишь уникальный идентификатор строки из таблицы «Студенты». В нашем случае это будет номер за- четной книжки — record_book. Данный атрибут и будет являться внешним ключом таблицы «Успеваемость». Таким образом, получив строку из таблицы «Студенты»,
можно будет найти все соответствующие ей строки в таблице «Успеваемость», сопо- ставив значения атрибутов record_book в строках обеих таблиц. В результате мы смо- жем получить все строки таблицы «Успеваемость», связанные с конкретной строкой из таблицы «Студенты» по внешнему ключу.
Таблица «Успеваемость» будет ссылающейся (referencing), а таблица «Студенты» —
ссылочной
(referenced). Обратите внимание, что внешний ключ ссылающейся таб- лицы ссылается на первичный ключ ссылочной таблицы. Допускается ссылка и на уникальный ключ, не являющийся первичным. В данном контексте для описания от- ношений между таблицами можно сказать, что таблица students является главной, а таблица progress — подчиненной.
Создать внешний ключ можно в формате ограничения уровня атрибута следующим образом:
CREATE TABLE progress
( record_book numeric( 5 ) REFERENCES students ( record_book ),
...
);
Предложение REFERENCES создает ограничение ссылочной целостности и указывает в качестве ссылочного ключа атрибут record_book. Это означает, что в таблицу «Успе- ваемость» (progress) нельзя ввести строку, значение атрибута record_book которой от- сутствует в таблице «Студенты» (students). Говоря простым языком, нельзя ввести за- пись об оценке того студента, информация о котором еще не введена в таблицу «Сту- денты».
Поскольку внешний ключ в нашем примере ссылается на первичный ключ, можно использовать сокращенную форму записи этого ограничения, не указывая список ат- рибутов:
CREATE TABLE progress
( record_book numeric( 5 ) REFERENCES students,
...
);
Можно определить внешний ключ и в форме ограничения уровня таблицы:
CREATE TABLE progress
( record_book numeric( 5 ),
...
75

FOREIGN KEY ( record_book )
REFERENCES students ( record_book )
);
Конечно, число атрибутов и их типы данных во внешнем ключе ссылающейся табли- цы и в первичном ключе ссылочной таблицы должны быть согласованы.
Ограничению внешнего ключа можно присвоить наименование, как и любому дру- гому ограничению, с помощью ключевого слова CONSTRAINT.
При наличии связей между таблицами, организованных с помощью внешних клю- чей, необходимо придерживаться определенной политики при выполнении опера- ций удаления и обновления строк в ссылочных таблицах, т. е. в тех, на которые ссы- лаются другие таблицы. В нашем примере ситуация принятия «политического» ре- шения возникает при удалении строк из таблицы «Студенты» (students). Конечно, ес- ли бы было принято решение хранить всю историю успеваемости студентов, в том числе и отчисленных, тогда строки из таблицы students вообще не удалялись бы. Но,
упрощая реальную ситуацию, мы решили историю не хранить. Тогда возникает за- кономерный вопрос: что делать со строками в таблице «Успеваемость» (progress), ко- торые ссылаются на удаляемую строку в таблице «Студенты» (students)? Возможны несколько вариантов.
1. Удаление связанных строк из таблицы «Успеваемость» (progress), что означает,
что при отчислении студента будет удаляться вся история его успехов в учебе.
Эта операция называется каскадным удалением и для ее реализации в опреде- ление внешнего ключа добавляются ключевые слова ON DELETE CASCADE. На- пример:
1   ...   5   6   7   8   9   10   11   12   ...   28


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