Бази даних-20210115T104840Z-001 / Реферат на тему _Современные СУБД_ / Using_MySql,_MS_SQL_Server_and_Oracle
.pdfПример 32: обеспечение консистентности данных |
|
||||
Oracl |
і |
Решение 4.1.2.b (проверка работоспособности) |
| |
||
e |
|||||
|
|
|
|
||
9 |
-- Изменение в добавленных связях значения идентификаторов книг, |
||||
10 |
-- без изменения значения идентификаторов жанров: |
||||
11 |
UPDATE "m2m books genres" |
|
|||
12 |
SET |
|
"b id" = 3 |
|
|
13 |
WHERE |
"b id" = 1 |
|
||
14 |
|
AND "g_id" = 4; |
|
||
15 |
|
|
|
|
|
16 |
UPDATE "m2m books genres" |
|
|||
17 |
SET |
|
"b id" = 4 |
|
18WHERE "b id" = 2
19AND "g_id" = 4;
21-- Изменение в добавленных связях значения идентификаторов жанров,
22-- без изменения значения идентификаторов книг:
23UPDATE "m2m books genres"
24 |
SET |
"g id" = 5 |
25WHERE "b id" = 3
26AND "g_id" = 4;
28 |
UPDATE "m2m books |
genres" |
|
29 |
SET |
"g id" = 5 |
|
30WHERE "b id" = 4
31AND "g_id" = 4;
33-- Изменение в добавленных связях значения идентификаторов жанров,
34-- и идентификаторов книг одновременно:
35UPDATE "m2m books genres"
36 |
SET |
"b id" = 1, |
37"g id" = 4
38WHERE "b id" = 3
39AND "g_id" = 5;
41 |
UPDATE "m2m books genres" |
|
42 |
SET |
"b id" = 2, |
43"g id" = 4
44WHERE "b id" = 4
45AND "g_id" = 5;
47-- Удаление ранее созданных связей:
48DELETE FROM "m2m books genres"
49WHERE "b id" = 1
50AND "g_id" = 4;
51
52DELETE FROM "m2m books genres"
53WHERE "b id" = 2
54AND "g_id" = 4;
55
56-- Удаление книг с идентификаторами 1 и 2 (обе эти книги одновременно
57-- относятся к жанрам «Поэзия» и «Классика»):
58DELETE FROM "books"
59WHERE "b _id" IN (1,2);
На этом решение данной задачи завершено.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 330/545
Пример 32: обеспечение консистентности данных
{292} |
{305} |
&4.1.2. a{292} и 4.1.2.b{292} таким образом, чтобы ни при каких манипуляциях с данными значения полей s_books (в таблице subscribers) и g_books (в таблице genres) не могли оказаться отрицательными.
&Задание 4.1.2.TSK.B: модифицировать схему базы данных «Библиотека» таким образом, чтобы таблица subscribers хранила информацию о том,задачЗадание 4.1.2.TSK.A: доработать триггеры из решений ,
сколько раз читатель брал в библиотеке книги (этот счётчик должен инкрементироваться каждый раз, когда читателю выдаётся книга; уменьшение значения этого счётчика не предусмотрено).
& Задание 4.1.2.TSK.C: оптимизировать код UPDATE-триггера из решения{292} задачи 4.1.2.a{292} для MS SQL Server так, чтобы выполнялась одна операция обновления таблицы subscribers (а не две отдельных операции, как это
реализовано сейчас).
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 331/545
Пример 33: контроль операций модификации данных
4.2.Контроль операций с данными с использованием триггеров
4.2.1. Пример 33: контроль операций модификации данных
'. Задача 4.2.1.a{315}: создать триггер, не позволяющий добавить в базу данных информацию о выдаче книги, если выполняется хотя бы одно из условий:
•дата выдачи находится в будущем;
•дата возврата находится в прошлом (только для вставки данных);
•дата возврата меньше даты выдачи.
;Задача 4.2.1.b{328}: создать триггер, не позволяющий выдать книгу читателю,
укоторого на руках находится десять и более книг.
'; Задача 4.2.1.c335: создать триггер, не позволяющий изменять значение поля sb_is_active таблицы subscriptions со значения N на значение Y.
Ожидаемый результат 4.2.1.a.
При попытке внести в базу данных изменения, противоречащие условию задачи, операция (транзакция) должна быть отменена. Также должно быть выведено сообщение об ошибке, наглядно поясняющее суть проблемы, например:
• |
“Date 2038.01.12 for subscription 145 |
activation isin the future”. |
• |
“Date 1983.01.12 for subscription 155 |
deactivationis in the past”. |
• |
“Date 2000.01.12 for subscription 165 |
deactivationis less than date for its activa |
|
tion (2015.01.12)”. |
|
|
Ожидаемый результат 4.2.1.b. |
|
При попытке внести в базу данных изменения, противоречащие условию задачи, операция (транзакция) должна быть отменена. Также должно быть выведено сообщение об ошибке, наглядно поясняющее суть проблемы, например: “Subscriber
Иванов И.И. (id = 1) already has 23 books out of 10 allowed.”
Ожидаемый результат 4.2.1.c.
При попытке внести в базу данных изменения, противоречащие условию задачи, операция (транзакция) должна быть отменена. Также должно быть выведено сообщение об ошибке, наглядно поясняющее суть проблемы, например: “It is prohibited to activate previously deactivated subscriptions (rule violated for subscriptions 34,
89, 12).”
xA?
Решение 4.2.1 .a{315}.
Для решения данной задачи нам понадобятся только INSERT- и UPDATE -
триггеры, т.к. в процессе удаления данных невозможно нарушить ни одно из контролируемых условий.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 332/545
Пример 33: контроль операций модификации данных
Код iNSERT-триггера для MySQL выглядит следующим образом.
|
MySQL і Решение 4.2.1.a (триггеры для таблицы subscriptions) |
[ |
I |
DELIMITER. $$ .............................. |
‘ .............................. |
2 |
|
|
CREATE TRIGGER 'subscriptions_control_ins'
4AFTER INSERT
5ON 'subscriptions'
6FOR EACH ROW
7BEGIN 8
9-- Блокировка выдач книг с датой выдачи в будущем.
10IF NEW.'sb_start' > CURDATE()
II |
THEN |
12 |
SET @msg = CONCAT('Date ', NEW.'sb_start', ' for subscription ', |
13 |
NEW.'sb_id', ' activation is in the future.'); |
14SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = @msg MYSQL_ERRNO = 1001
15END IF; 16
17-- Блокировка выдач книг с датой возврата в прошлом.
18IF NEW.'sb_finish' < CURDATE()
19THEN
20SET @msg = CONCAT('Date ', NEW.'sb_finish', ' for subscription ',
21 |
NEW.'sb_id', ' deactivation is in the past.'); |
22SIGNAL SQLSTATE '45002' SET MESSAGE_TEXT = @msg MYSQL_ERRNO = 1002
23END IF;
24
25-- Блокировка выдач книг с датой возврата меньшей, чем дата выдачи.
26IF NEW.'sb_finish' < NEW.'sb_start'
27THEN
28SET @msg = CONCAT('Date ', NEW.'sb_finish', ' for subscription ',
29 |
NEW.'sb_id', |
30 |
' deactivation is less than the date for its activation (', |
31 |
NEW.'sb_start', ').'); |
32SIGNAL SQLSTATE '45003' SET MESSAGE_TEXT = @msg MYSQL_ERRNO = 1003
33END IF; 34
35END;
36$$ 37
38DELIMITER ;
Сточки зрения функциональности в данном случае можно было бы использовать и BEFORE-триггер, но в случае с AFTER-триггером сообщение об ошибке по-
лучается более информативным, т.к. уже содержит в себе корректное значение автоинкрементируемого первичного ключа (в BEFORE-триггере это значение не опре-
делено, и потому в сообщении об ошибке превращается в 0).
Код в строках 25-33 в настоящий момент не нужен — две предшествующих проверки не допустят возникновения проверяемой этим кодом ситуации. Но если в будущем эти проверки будут модифицированы или убраны, код в строках 25-33 будет срабатывать.
Поскольку в MySQL триггер не может явно отменить транзакцию, мы порождаем исключительную ситуацию (строки 14, 22, 23), при возникновении которой отменяется транзакция, активировавшая срабатывание триггера.
Код UPDATE-триггера будет даже чуть более простым, т.к. в нём по условию
задачи нет необходимости проверять, находится ли дата возврата в прошлом. При этом код в строках 17-25 (ранее отмеченный как бесполезный для IN-
SERT-триггера) здесь будет срабатывать, т.к. по условию задачи при выполнении операции обновления допускается установка в поле sb_finish даты из прошлого, что позволяет нарушить третье условие задачи.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 333/545
Пример 33: контроль операций модификации данных
MySQL I |
Решение 4.2.1.a (триггеры для таблицы subscriptions) | |
1 DELIMITER $$
2
3CREATE TRIGGER 'subscriptions_control_upd'
4AFTER UPDATE
5ON 'subscriptions'
6FOR EACH ROW
7BEGIN
8
9-- Блокировка выдач книг с датой выдачи в будущем.
10IF NEW.'sb start' > CURDATE()
11THEN
12SET @msg = CONCAT('Date ', NEW.'sb start', ' for subscription ',
13 |
NEW.'sb id', ' activation is in the future.'); |
14SIGNAL SQLSTATE '45001' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1001;
15END IF;
16
17-- Блокировка выдач книг с датой возврата меньшей, чем дата выдачи.
18IF NEW.'sb finish' < NEW.'sb start'
19THEN
20SET @msg = CONCAT('Date ', NEW.'sb finish', ' for subscription ',
21 |
NEW. 'sb id', |
22 |
' deactivation is less than the date for its activation (', |
23 |
NEW.'sb start', ').'); |
24SIGNAL SQLSTATE '45003' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1003;
25END IF;
26
27END;
28$$
29
30 DELIMITER ;
Проверим работоспособность полученного решения. Будем выполнять следующие запросы и следить за реакцией СУБД.
Попытаемся добавить выдачу книги с датой активации в будущем:
|
MySQL |
Решение 4.2.1 .а (проверка |
|
|
|
|
|
1 |
|
работоспособности) |
|
INSERT INTO |
|
||
2 |
VALUES |
(500, |
|
3 |
|
|
1, |
4 |
|
|
1, |
5 |
|
|
'2020-01-12', |
6 |
|
|
'2020-02-12', |
7 |
|
|
'N') |
|
|
|
|
СУБД запретит эту операцию, вернув следующее сообщение об ошибке:
Error Code: 1001. Date 2020-01-12 for subscription 500 activation is in the future.
Попытаемся добавить выдачу книги с датой активации в будущем, при этом не указав значение первичного ключа:
MySQL I |
’ешение 4.2.1 .a (проверка работоспособности) |
і |
|
1 |
INSERT |
INTO 'subscriptions' |
|
2 |
|
('sb id', |
|
3 |
|
'sb subscriber', |
|
4 |
|
'sb book' |
|
5 |
|
'sb start', |
|
6 |
|
'sb finish', |
|
7 |
|
'sb is active') |
|
8 |
VALUES |
(NULL, |
|
9 |
|
3, |
|
10 |
|
3, |
|
11 |
|
'2020-01-12', |
|
12 |
|
'2020-02-12', |
|
13 |
|
'N'); |
|
|
|
|
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 334/545
Пример 33: контроль операций модификации данных
СУБД запретит эту операцию, вернув следующее сообщение об ошибке:
Error Code: 1001. Date 2020-01-12 for subscription 501 activation is in the future.
Попытаемся добавить выдачу книги с датой возврата в прошлом:
MySQL і |
Решение 4.2.1.a (проверка работоспособности) |
|
1 |
INSERT INTO 'subscriptions' (502 |
|
2 |
VALUES |
1, 1, '2000-01-12', '2000-02-12', 'N' ) |
3
4
5
6
7
СУБД запретит эту операцию, вернув следующее сообщение об ошибке:
Error Code: 1002. Date 2000-02-12 for subscription 502 deactivation is in the past.
Попытаемся добавить выдачу книги, не нарушая ни одного из условий данной задачи:
MySQL і Решение 4.2.1.a (проверка работоспособности)
1 |
INSERT INTO 'subscriptions' |
|
2 |
VALUES |
(503 |
3 |
|
1, |
4 |
|
1, |
5 |
|
'2000-01-12', |
6 |
|
'2020-02-12', |
7 |
|
'N' ) |
|
|
|
СУБД позволит выполнить вставку.
Попытаемся обновить добавленную выдачу книги так, чтобы дата её активации оказалась в будущем:
MySQL і |
Решение 4.2.1.a (проверка работоспособности) |
|
1 |
UPDATE |
'subscriptions' |
2 |
SET |
'sb start' = '2020-01-01' |
3 |
WHERE 'sb id' = 503 |
СУБД запретит эту операцию, вернув следующее сообщение об ошибке:
Error Code: 1001. Date 2020-01-01 for subscription 503 activation is in the future.
Попытаемся обновить добавленную выдачу книги так, чтобы дата её активации оказалась позже даты возврата:
MySQL і Решение 4.2.1.a (проверка работоспособности)
1 UPDATE 'subscriptions'
2 SET 'sb_start' = '2010-01-01',
3'sb finish' = '2005-01-01'
4WHERE 'sb id' = 503
СУБД запретит эту операцию, вернув следующее сообщение об ошибке:
Error Code: 1003. Date 2005-01-01 for subscription 503 deactivation is less than the date for its activation (2010-01-01).
Попытаемся обновить добавленную выдачу книги так, чтобы дата её возврата была в прошлом (для операции обновления такое разрешено):
MySQL |
Решение 4.2.1 .а (проверка |
|
1 |
UPDATEработоспособности) : |
|
2 |
SET |
'subscriptions' |
3'sb_start' = '2005-01-01',
4WHERE 'sb_finish' = '2006-01-01'
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 335/545
Пример 33: контроль операций модификации данных
СУБД позволит выполнить обновление.
Попытаемся обновить добавленную выдачу книги, не нарушая ни одного из условий задачи:
MySQL |
Решение 4.2.1 .а (проверка работоспособности) |
|
1 |
UPDATE 'subscriptions' |
|
2 |
SET |
'sb_start' = '2005-01-01', |
3'sb finish' = '2010-01-01'
4WHERE 'sb id' = 503
СУБД позволит выполнить обновление.
Итак, решение данной задачи для MySQL готово и проверено. Переходим к решению для MS SQL Server.
В MS SQL Server нам доступны только триггеры уровня выражения, потому их внутренняя логика будет иной, нежели в MySQL. Также несколько иначе будут выглядеть возвращаемые триггерами сообщения об ошибках, т.к. в них нужно будет отразить информацию обо всех выдачах книг: в одном варианте реализации — только о «плохих», во втором — и о «плохих», и о «хороших».
Первый вариант реализации триггера полностью блокирует операцию, если хотя бы одна из записей нарушает условия задачи. Информация о таких записях аккумулируется в строковой переменной @bad_records (пояснение о логике ра-
боты функции STUFF см. в решении{72} задачи 2.2.2.а{71}).
Далее проверяется длина полученной строки: если она не равна нулю, значит, «плохие» записи обнаружены, и триггер должен отправить клиенту сообщение об ошибке (строки 23, 48, 67) и отменить операцию («откатить транзакцию») (строки
24, 49, 68).
В строках 28-32 мы получаем информацию о количестве записей в псевдотаблицах inserted и deleted, чтобы затем в строках 42-43 определить, выполнялась ли операция вставки (её признак: нет записей в deleted, есть записи в inserted).
MS SQL |
Решение 4.2.1 .а (триггеры для таблицы subscriptions, первый вариант |
1-- Вариант с полной блокировкойрешенияоперации) .
2CREATE TRIGGER [subscriptions control]
3ON [subscriptions]
4AFTER INSERT, UPDATE
5AS
6-- Переменные для хранения списка "плохих записей" и сообщения об ошибке.
7DECLARE @bad records NVARCHAR(max);
8DECLARE @msg NVARCHAR(max);
9
10-- Блокировка выдач книг с датой выдачи в будущем.
11SELECT @bad records = STUFF((SELECT ', ' + CAST [sb id] AS NVARCHAR) +
12 |
|
' (' + CAST([sb start] AS NVARCHAR) + ')' |
13 |
FROM |
[inserted] |
14 |
WHERE [sb start] > CONVERT(date, GETDATE()) |
|
15 |
ORDER BY [sb id] |
|
16 |
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), |
|
17 |
1, 2 |
''); |
18IF LEN(@bad records) > 0
19BEGIN
20SET @msg =
21CONCAT('The following subscriptions'' activation dates are
22in the future: ', @bad records);
23 RAISERROR @msg 16 1);
24ROLLBACK TRANSACTION;
25RETURN
26END;
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 336/545
Пример 33: контроль операций модификации данных
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 337/545
Пример 33: контроль операций модификации данных
MS SQL I |
Решение 4.2.1.a (триггеры для таблицы subscriptions, первый вариант решения) (продолжение) |
| |
27-- Блокировка выдач книг с датой возврата в прошлом.
28DECLARE @deleted records INT;
29DECLARE @inserted records INT;
30
31SELECT @deleted records = COUNT(*) FROM [deleted];
32SELECT @inserted records = COUNT(*) FROM [inserted];
34SELECT @bad records = STUFF((SELECT ', ' + CAST [sb id] AS NVARCHAR) +
35 |
' (' + CAST([sb start] AS NVARCHAR) + ')' |
|
36 |
FROM |
[inserted] |
37 |
WHERE [sb finish] < CONVERT(date, GETDATE()) |
|
38 |
ORDER BY [sb id] |
|
39 |
FOR XML PATH(''), TYPE) value('.', 'nvarchar(max)'), |
|
40 |
1, 2 |
''); |
41IF ((LEN @bad records > 0) AND
42@deleted records = 0 AND
43@inserted records > 0))
44BEGIN
45SET @msg =
46CONCAT('The following subscriptions'' deactivation dates are
47in the past: ', @bad records);
48 RAISERROR @msg 16 1 ;
49ROLLBACK TRANSACTION;
50RETURN
51END;
52
53-- Блокировка выдач книг с датой возврата меньшей, чем дата выдачи.
54SELECT @bad records = STUFF((SELECT ', ' + CAST [sb id] AS NVARCHAR) +
55' (act: ' + CAST [sb start] AS NVARCHAR) + ', deact: ' +
56CAST [sb finish] AS NVARCHAR) + ')'
57 |
FROM |
[inserted] |
|
|
58 |
WHERE [sb finish] |
< [sb |
start] |
|
59 |
ORDER BY [sb id] |
|
|
|
60 |
FOR XML PATH(''), |
TYPE) |
value('.', 'nvarchar(max)'), |
|
61 |
1, 2 |
''); |
|
|
62IF LEN(@bad records) > 0
63BEGIN
64SET @msg =
65CONCAT('The following subscriptions'' deactivation dates are less
66 |
than activation dates: ', @bad records ; |
67 |
RAISERROR @msg 16 1 ; |
68ROLLBACK TRANSACTION;
69RETURN
70END;
71GO
Второй вариант реализации триггера блокирует только операции с «плохими» записями, а операции с «хорошими» записями выполняет. Также он выводит сообщение с информацией о «хороших» записях и сообщение об ошибке с информацией о «плохих».
Чтобы добиться такого эффекта мы будем использовать INSTEAD OF триг-
гер, который активируется вместо соответствующей операции с данными. Если в теле такого триггера не выполнять никаких действий, то исходная операция с данными не выполнится по определению, и даже нет надобности «откатывать транзакцию». Чтобы операция выполнилась, в теле триггера нужно будет выполнить соответствующий запрос на модификацию данных в таблице.
К сожалению, в таблице subscriptions есть внешние кличи с каскадным обновлением, потому MS SQL Server не позволит создать INSTEAD OF UPDATE
триггер, но продемонстрировать общую логику решения можно и на одной лишь операции вставки.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 338/545
Пример 33: контроль операций модификации данных
Если бы перед нами остро стояла задача применить именно этот вариант решения и для операции обновления, мы могли бы изменить свойства внешних ключей, убрав там операцию каскадного обновления и реализовав её отдельным триггером.
Обратите внимание на то, как в данном варианте решения реализована последовательность действий: поскольку мы не отменяем операцию и не выходим из тела триггера (как это реализовано в строках 24-25, 49-50, 68-69 первого варианта решения), триггер доработает до конца своего тела, что вынуждает нас одновременно учитывать все три условия задачи при принятии решения о том, «хорошая» ли нам попалась запись или «плохая».
В первую очередь мы получаем три списка «плохих» записей — по каждому из условий задачи (строки 14-40). В INSTEAD OF триггере нам неизвестны значения автоинкрементируемого первичного ключа (IDENTITY-ПОЛЯ sb_id), потому мы со-
бираем только сами значения дат. Однако, для реальной вставки данных (строки 81105) эти значения нам понадобятся: в решении{246} задачи 3.2.1.a{245} подробно объяснена логика их получения и использования.
MS |
QL | |
решение 4.2.1 .a (триггер для таолицы subscriptions, второй вариант решения) |
І |
|
|||
1 |
-- Вариант с частичной блокировкой операции. |
|
|
2 |
CREATE TRIGGER [subscriptions_control] |
|
|
3 |
ON [subscriptions] INSTEAD OF INSERT AS |
|
|
4 |
-- Переменные для хранения сообщений и списков записей. |
|
|
5 |
DECLARE @bad_records_act_future NVARCHAR(max); |
|
|
6 |
DECLARE @bad_records_deact_past NVARCHAR(max); |
|
|
7 |
DECLARE @bad_records_act_greater_than_deact NVARCHAR(max); |
|
|
8 |
|
|
|
9 |
DECLARE @good_records NVARCHAR(max); |
|
|
10 |
DECLARE @msg NVARCHAR(max); |
|
|
11 |
|
|
|
12-- Блокировка выдач книг с датой выдачи в будущем.
13SELECT @bad_records_act_future =
14 |
STUFF((SELECT ', ' + CAST [sb_start] AS NVARCHAR) |
|||
15 |
FROM |
[inserted] |
||
16 |
WHERE [sb_start] > CONVERT(date, GETDATE()) |
|||
17 |
ORDER BY |
[sb_start] |
||
18 |
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), |
|||
19 |
1, 2 |
''); |
|
|
20 |
|
|
|
|
23 |
STUFF((SELECT ', ' + CAST [sb_finish] AS NVARCHAR) |
|||
|
|
|
||
24 |
FROM |
[inserted] |
||
|
|
|
||
25 |
WHERE |
[sb_finish] < CONVERT(date, GETDATE()) |
||
|
|
|
||
26 |
ORDER BY |
[sb_finish] |
||
|
|
|
||
27 |
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), |
|||
|
|
|
||
28 |
1, 2 |
''); |
|
|
|
|
|
||
29 |
-- Блокировка выдач книг с датой возврата меньшей, чем дата выдачи. |
|||
30 |
||||
SELECT @bad_records_act_greater_than_deact = |
||||
31 |
||||
STUFF((SELECT ', (act: ' + CAST([sb_start] AS NVARCHAR) + |
||||
32 |
||||
|
', deact: ' + CAST [sb_finish] AS NVARCHAR) + ')' |
|||
33 |
|
|||
FROM |
[inserted] |
|||
34 |
||||
WHERE |
[sb_finish] < [sb_start] |
|||
35 |
||||
ORDER BY |
[sb_start], [sb_finish] |
|||
36 |
||||
21 |
-- Блокировка выдач книг с датой возврата в прошлом. |
|||
22 |
SELECT @bad_records_deact_past = |
|||
|
|
|
||
37 |
|
FOR XML PATH(''), TYPE).value('.', |
||
|
|
'nvarchar(max)' |
||
38 |
|
|
||
|
) |
|
||
39 |
|
|
||
1, 2 |
''); |
|
||
40 |
|
|||
|
|
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 339/545