Пример 24: слияние данных
2.3.4. Пример 24: слияние данных
|
Задача 2.3.4.a{195}: добавить в базу данных жанры «Философия», «Детек- |
|||||
|
тив», «Классика». |
|||||
|
Задача 2.3.4.b{197}: скопировать (без повторений) в базу данных «Библио- |
|||||
|
тека» содержимое таблицы |
genres |
из базы данных «Большая библио- |
|||
|
тека»; в случае совпадения первичных ключей добавить к существую- |
|||||
|
щему имени жанра слово « [OLD]». |
|||||
|
|
|
||||
|
Ожидаемый результат 2.3.4.a: содержимое таблицы |
genres |
должно при- |
|||
|
нять следующий вид (обратите внимание: жанр «Классика» уже был в |
|||||
|
этой таблице, и он не должен дублироваться). |
|||||
|
|
|
||||
g_id |
g_name |
|
||||
1 |
Поэзия |
|
||||
2 |
Программирование |
|
||||
3 |
Психология |
|
|
|
|
|
4 |
Наука |
|
|
|
|
|
5 |
Классика |
|
|
|
|
|
6 |
Фантастика |
|
|
|
|
|
7 |
Философия |
|
|
|
|
|
8 |
Детектив |
|
|
|
|
|
|
Ожидаемый результат 2.3.4.b: этот результат получен на «эталонных |
|||||
|
данных»; если вы сначала решите задачу 2.3.4.a, в вашем ожидаемом |
|||||
|
результате будут находиться все восемь жанров из «Библиотеки» и 92 |
|||||
|
жанра со случайными именами из «Большой библиотеки». |
|||||
|
|
|
||||
g_id |
g_name |
|||||
1Поэзия [OLD]
2Программирование [OLD]
3Психология [OLD]
4Наука [OLD]
5Классика [OLD]
6Фантастика [OLD]
Иещё 94 строки со случайными именами жанров из «Большой библиотеки»
Решение 2.3.4.a{195}.
Во всех трёх СУБД на поле g_name таблицы genres построен уникальный индекс, чтобы исключить возможность появления одноимённых жанров.
Эту задачу можно решить, последовательно выполнив три INSERT-запроса, отслеживая их результаты (для вставки жанра «Классика» запрос завершится ошибкой). Но можно реализовать и более элегантное решение.
MySQL поддерживает оператор REPLACE, который работает аналогично INSERT, но учитывает значения первичного ключа и уникальных индексов, добавляя новую строку, если совпадений нет, и обновляя имеющуюся, если совпадение есть.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 195/545
Пример 24: слияние данных
MySQL |
Решение 2.3.4.a |
||
1 |
REPLACE INTO `genres` |
||
2 |
|
|
(`g_id`, |
3 |
|
|
`g_name`) |
4 |
VALUES |
(NULL, |
|
5 |
|
|
'Философия'), |
6 |
|
|
(NULL, |
7 |
|
|
'Детектив'), |
8 |
|
|
(NULL, |
9 |
|
|
'Классика') |
При таком подходе за один запрос (который выполняется без ошибок) мы можем передать много новых данных, не опасаясь проблем, связанных с дублированием первичных ключей и уникальных индексов.
К сожалению, MS SQL Server и Oracle не поддерживают оператор REPLACE,
но аналогичного поведения можно добиться с помощью оператора MERGE.
MS SQL Решение 2.3.4.a
1MERGE INTO [genres]
2USING ( VALUES (N'Философия'),
3 |
|
(N'Детектив'), |
4 |
|
(N'Классика') ) AS [new_genres]([g_name]) |
5ON [genres].[g_name] = [new_genres].[g_name]
6WHEN NOT MATCHED BY TARGET THEN
7INSERT ([g_name])
8VALUES ([new_genres].[g_name]);
Вэтом запросе строки 2-4 представляют собой нетривиальный способ динамического создания таблицы, т.к. оператор MERGE не может получать «на вход» ни-
чего, кроме таблиц и условия их слияния.
Строка 5 описывает проверяемое условие (совпадение имени нового жанра с именем уже существующего), а строки 6-8 предписывают выполнить вставку данных в целевую таблицу только в том случае, когда условие не выполнилось (т.е. дублирования нет).
Обратите внимание на то, что в конце строки 8 присутствует символ ;. MS SQL Server требует его наличия в конце оператора MERGE.
Oracle Решение 2.3.4.a
1MERGE INTO "genres"
2USING (SELECT ( N'Философия' ) AS "g_name"
3 |
FROM |
dual |
4UNION
5SELECT ( N'Детектив' ) AS "g_name"
6 |
FROM |
dual |
7UNION
8SELECT ( N'Классика' ) AS "g_name"
9 |
FROM |
dual) "new_genres" |
10ON ( "genres"."g_name" = "new_genres"."g_name" )
11WHEN NOT MATCHED THEN
12INSERT ("g_name")
13VALUES ("new_genres"."g_name")
Решение для Oracle построено по той же логике, что и решение для MS SQL Server. Отличие состоит только в способе динамического формирования таблицы (строки 2-9) из новых данных.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 196/545
Пример 24: слияние данных
Решение 2.3.4.b{195}.
Для решения этой задачи в MySQL вам нужно установить соединение с СУБД от имени пользователя, имеющего права на работу с обеими базами данных (предположим, что «Библиотека» у вас называется `library`, а «Большая библиотека»
— `huge_library`).
Далее остаётся воспользоваться вполне классическим INSERT ... SELECT (позволяет вставить в таблицу данные, полученные в результате выполнения запроса; строки 1-8), а условие задачи о добавлении слова « [OLD]» реализовать через специальный синтаксис ON DUPLICATE KEY UPDATE (строки 9-11), предписывающий MySQL выполнять обновление записи в целевой таблице, если возникает ситуация дублирования по первичному ключу или уникальному индексу между уже существующими и добавляемыми данными.
|
MySQL |
Решение 2.3.4.b |
||
|
1 |
|
INSERT INTO `library`.`genres` |
|
|
2 |
|
|
( |
|
3 |
|
|
`g_id`, |
|
4 |
|
|
`g_name` |
|
5 |
|
|
) |
6SELECT `g_id`,
7`g_name`
8 FROM `huge_library`.`genres`
9ON DUPLICATE KEY
10UPDATE `library`.`genres`.`g_name` =
11CONCAT(`library`.`genres`.`g_name`, ' [OLD]')
Для решения этой задачи в MS SQL Server (как и в случае с MySQL) вам нужно установить соединение с СУБД от имени пользователя, имеющего права на работу с обеими базами данных (предположим, что «Библиотека» у вас называется
[library], а «Большая библиотека» — [huge_library]).
MS SQL Server не поддерживает ON DUPLICATE KEY UPDATE, но аналогичного поведения можно добиться с использованием MERGE.
MS SQL Решение 2.3.4.b
1-- Разрешение вставки явно переданных значений в IDENTITY-поле:
2SET IDENTITY_INSERT [genres] ON;
3 |
|
|
|
4 |
|
-- Слияние данных: |
|
5 |
|
MERGE [library].[dbo].[genres] |
AS [destination] |
6USING [huge_library].[dbo].[genres] AS [source]
7ON [destination].[g_id] = [source].[g_id]
8WHEN MATCHED THEN
9UPDATE SET [destination].[g_name] =
10 |
|
CONCAT([destination].[g_name], N' [OLD]') |
11WHEN NOT MATCHED THEN
12INSERT ([g_id],
13[g_name])
14VALUES ([source].[g_id],
15[source].[g_name]);
16
17-- Запрет вставки явно переданных значений в IDENTITY-поле:
18SET IDENTITY_INSERT [genres] OFF;
Поскольку поле g_id является IDENTITY-полем, нужно явно разрешить вставку туда данных (строка 2) перед выполнением вставки, а затем запретить (строка 18).
Строки 5-7 запроса описывают таблицу-источник, таблицу-приёмник и проверяемое условие совпадения значений.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 197/545
Пример 24: слияние данных
Строки 8-10 и 11-15 описывают, соответственно, реакцию на совпадение (выполнить обновление) и несовпадение (выполнить вставку) первичных ключей таб- лицы-источника и таблицы-приёмника.
Для решения этой задачи в Oracle, (как и в случае с MySQL и MS SQL Server) вам нужно установить соединение с СУБД от имени пользователя, имеющего права на работу с обеими базами данных (предположим, что «Библиотека» у вас называ-
ется "library", а «Большая библиотека» — "huge_library").
Oracle как и MS SQL Server не поддерживает ON DUPLICATE KEY UPDATE,
но аналогичного поведения можно добиться с использованием MERGE.
Решение для Oracle аналогично решению для MS SQL Server, за исключением необходимости отключать (строка 2) перед вставкой данных триггер, отвечающий за автоинкремент первичного ключа, и снова включать его (строка 18) после вставки.
Oracle Решение 2.3.4.b
1-- Отключение триггера, обеспечивающего автоинкремент первичного ключа:
2ALTER TRIGGER "library"."TRG_genres_g_id" DISABLE;
3
4-- Слияние данных:
5MERGE INTO "library"."genres" "destination"
6USING "hude_library"."genres" "source"
7ON ("destination"."g_id" = "source"."g_id")
8WHEN MATCHED THEN
9UPDATE SET "destination"."g_name" =
10CONCAT("destination"."g_name", N' [OLD]')
11WHEN NOT MATCHED THEN
12INSERT ("g_id",
13"g_name")
14VALUES ("source"."g_id",
15"source"."g_name");
16
17-- Включение триггера, обеспечивающего автоинкремент первичного ключа:
18ALTER TRIGGER "library"."TRG_genres_g_id" ENABLE;
Если по неким причинам вы не можете соединиться с СУБД от имени пользователя, имеющего доступ к обеим интересующим вас схемам, можно пойти по следующему пути (такие варианты не были рассмотрены для MySQL и MS SQL Server, т.к. в этих СУБД нет поддержки некоторых возможностей, а сложность обходных решений выходит за рамки данной книги — на порядки проще создать пользователя с правами доступа к обеим базам данных (схемам)).
Строки 6-23 представленного ниже большого набора запросов аналогичны только что рассмотренному решению, а весь остальной код (строки 1-4, 25-37) нужен для того, чтобы обеспечить возможность одновременного доступа к данным в двух разных схемах.
Строки 2-4 отвечают за создание и открытие соединения к схеме-источнику.
Встроке 26 команда COMMIT нужна потому, что без неё при закрытии соединения мы рискуем потерять часть данных.
Встроках 29 и 32-34 представлены два варианта закрытия соединения. Как правило, срабатывает первые вариант, но если не сработал — есть второй.
Встроке 37 ранее созданное соединение уничтожается.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 198/545
Пример 24: слияние данных
Oracle Решение 2.3.4.b (работа от имени двух пользователей)
1-- Создание соединения со схемой-источником:
2CREATE DATABASE LINK "huge"
3CONNECT TO "логин" IDENTIFIED BY "пароль"
4USING 'localhost:1521/xe';
5
6-- Отключение триггера, обеспечивающего автоинкремент первичного ключа:
7ALTER TRIGGER "TRG_genres_g_id" DISABLE;
8
9-- Слияние данных:
10MERGE INTO "genres" "destination"
11USING "genres"@"huge" "source"
12ON ("destination"."g_id" = "source"."g_id")
13WHEN MATCHED THEN
14UPDATE SET "destination"."g_name" =
15 |
CONCAT("destination"."g_name", N' [OLD]') |
16WHEN NOT MATCHED THEN
17INSERT ("g_id",
18"g_name")
19VALUES ("source"."g_id",
20"source"."g_name");
21
22-- Включение триггера, обеспечивающего автоинкремент первичного ключа:
23ALTER TRIGGER "TRG_genres_g_id" ENABLE;
24
25-- Явное подтверждение сохранения всех изменений:
26COMMIT;
27
28-- Закрытие соединения со схемой источником:
29ALTER SESSION CLOSE DATABASE LINK "huge";
30
31-- Если не помогло ALTER SESSION CLOSE ... :
32BEGIN
33DBMS_SESSION.CLOSE_DATABASE_LINK('huge');
34END;
35
36-- Удаление соединения со схемой-источником:
37DROP DATABASE LINK "huge";
Задание 2.3.4.TSK.A: добавить в базу данных жанры «Политика», «Психология», «История».
Задание 2.3.4.TSK.B: скопировать (без повторений) в базу данных «Библиотека» содержимое таблицы subscribers из базы данных «Большая библиотека»; в случае совпадения первичных ключей добавить к существующему имени читателя слово « [OLD]».
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 199/545