Бази даних-20210115T104840Z-001 / Реферат на тему _Современные СУБД_ / Using_MySql,_MS_SQL_Server_and_Oracle
.pdfПример 33: контроль операций модификации данных
MySQL I |
Решение 4.2.1.b (триггеры для таблицы subscriptions) | |
|||
1 |
DELIMITER $$ |
|
|
|
2 |
|
|
|
|
3 |
CREATE TRIGGER 'sbs cntrl 10 books ins OK' |
|||
4 |
BEFORE INSERT |
|
|
|
5 |
ON 'subscriptions' |
|
|
|
6 |
|
FOR EACH ROW |
|
|
7 |
|
BEGIN |
|
|
8 |
|
|
|
|
9 |
|
SET @msg = IFNULL((SELECT CONCAT('Subscriber ', 's name', |
||
10 |
|
|
' (id=', 'sb subscriber', ') already has ', |
|
11 |
|
|
'sb books', ' books out of 10 allowed.') |
|
12 |
|
|
AS 'message' |
|
13 |
|
FROM |
(SELECT 'sb subscriber', |
|
14 |
|
|
|
COUNT('sb book') AS 'sb books' |
15 |
|
|
FROM |
'subscriptions' |
16 |
|
|
WHERE |
'sb is active' = 'Y' |
17 |
|
|
AND 'sb subscriber' = NEW.'sb subscriber' |
|
18 |
|
|
GROUP |
BY 'sb subscriber' |
19 |
|
|
HAVING 'sb books' >= 10 AS 'prepared data' |
|
20 |
|
JOIN 'subscribers' |
||
21 |
|
ON 'sb subscriber' = 's id'), |
||
22 |
|
''); |
|
|
23 |
|
|
|
|
24IF (LENGTH @msg > 0)
25THEN
26SIGNAL SQLSTATE '45001' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1001;
27END IF;
28
29END;
30$$
31
32CREATE TRIGGER 'sbs_cntrl_10_books_upd_OK'
33BEFORE UPDATE
34ON 'subscriptions'
35FOR EACH ROW
36BEGIN
37 |
|
|
|
38 |
SET @msg = IFNULL((SELECT CONCAT('Subscriber ', 's name', |
||
39 |
|
' (id=', 'sb subscriber', ') already has ', |
|
40 |
|
'sb books', ' books out of 10 allowed.') |
|
41 |
|
AS 'message' |
|
42 |
FROM |
(SELECT 'sb subscriber', |
|
43 |
|
|
COUNT('sb book') AS 'sb books' |
44 |
|
FROM |
'subscriptions' |
45 |
|
WHERE |
'sb is active' = 'Y' |
46 |
|
AND 'sb subscriber' = NEW.'sb subscriber' |
|
47 |
|
GROUP |
BY 'sb subscriber' |
48 |
|
HAVING 'sb books' >= 10 AS 'prepared data' |
|
49 |
JOIN 'subscribers' |
||
50 |
ON 'sb subscriber' = 's id'), |
||
51 |
''); |
|
|
52 |
|
|
|
53IF (LENGTH @msg > 0)
54THEN
55SIGNAL SQLSTATE '45001' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1001;
56END IF;
57
58END;
59$$
60
61DELIMITER ;
Вправильном решении для MySQL мы реагируем только на выдачи книг для читателя, идентификатор которого фигурирует в добавляемой/изменяемой записи. Также проверку мы выполняем перед тем, как изменения вступят в силу, и потому
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 350/545
Пример 33: контроль операций модификации данных
СУБД даже не придётся их отменять, если операция будет запрещена (что также сэкономит немного времени).
Исследование 4.2.1.EXP.A. Проведём исследование на базе данных «Большая библиотека», сравнив скорость работы представленных неправильного и правильного решений для MySQL.
После выполнения тысячи запросов на вставку данных, нарушающих условие задачи, медианы времени (в секундах) приняли следующие значения:
Неправильное решение |
Правильное решение |
Разница, раз |
51.177 |
0.009 |
5686 |
Переходим к решению для MS SQL Server, в котором также представим два варианта — неправильный и правильный.
Неправильный вариант полностью повторяет аналогичную неверную логику решения для MySQL — мы пытаемся анализировать полный набор информации в базе данных, к тому же делаем это после выполнения операции модификации данных, заставляя СУБД отменять полученные изменения.
MS SQL І |
Решение 4.2.1.b (неправильное решение) |
| |
1CREATE TRIGGER [sbs cntrl 10 books ins upd WRONG]
2ON [subscriptions]
3AFTER INSERT, UPDATE
4AS
5DECLARE @bad records NVARCHAR(max);
6DECLARE @msg NVARCHAR(max);
7 |
|
|
8 |
SELECT @bad records = STUFF((SELECT ', ' + [list] |
|
9 |
FROM (SELECT CONCAT('(id=', [s id], ', ', |
|
10 |
|
[s name], ', books=', |
11 |
|
COUNT([sb book]), ')') AS [list] |
12 |
FROM |
[subscribers] |
13 |
|
JOIN [subscriptions] |
14 |
|
ON [s id] = [sb subscriber] |
15 |
WHERE |
[sb is active] = 'Y' |
16 |
GROUP |
BY [s id], [s name] |
17 |
HAVING COUNT([sb book]) > 10) |
|
18 |
AS [prepared data] |
|
19 |
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), |
|
20 |
1, 2 ''); |
|
21 |
|
|
22IF (LEN @bad records I > 0
23BEGIN
24SET @msg = CONCAT('The following readers have more books
25 |
than allowed (10 allowed): ', @bad records) ; |
26 |
RAISERROR @msg 16 1); |
|
|
27ROLLBACK TRANSACTION;
28RETURN;
29END;
30GO
Правильный вариант решения для MS SQL Server подвержен ограничениям, подробно описанным в решении{315} задачи 4.2.1.a{315} (невозможность создания IN- STEAD OF UPDATE триггера без отключения операции каскадного обновления на
внешних ключах, необходимость вычислять значение первичного ключа), потому здесь мы также ограничимся созданием только iNSERT-триггера.
Однако, благодаря тому, что мы ограничиваем анализ выполнения условия задачи только перечнем читателей, чьи идентификаторы находятся в псевдотаблице inserted, а также выполняем этот анализ до реальной вставки данных, есть шанс существенно повысить скорость работы нашего триггера.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 351/545
Пример 33: контроль операций модификации данных
MS SQL I |
Решение 4.2.1.b (триггер для таблицы subscriptions) |
| |
1CREATE TRIGGER [sbs cntrl 10 books ins OK]
2ON [subscriptions]
3INSTEAD OF INSERT
4AS
5DECLARE @bad records NVARCHAR(max);
6DECLARE @msg NVARCHAR(max);
7 |
|
|
|
|
8 |
SELECT @bad records = STUFF((SELECT ', ' + [list] |
|||
9 |
FROM |
(SELECT CONCAT('(id=', [s id], ', ', |
||
10 |
|
|
[s name] ', books=', |
|
11 |
|
|
COUNT([sb book]), ')') AS [list] |
|
12 |
|
FROM |
[subscribers] |
|
13 |
|
|
JOIN [subscriptions] |
|
14 |
|
|
ON [s id] = [sb subscriber] |
|
15 |
|
WHERE |
[sb is active] = 'Y' |
|
16 |
|
|
AND [sb subscriber] IN |
|
17 |
|
|
(SELECT [sb subscriber] |
|
18 |
|
|
FROM |
[inserted]) |
19 |
|
GROUP |
BY [s id], [s name] |
|
20 |
|
HAVING COUNT([sb book]) >= 10) |
||
21 |
|
AS [prepared data] |
|
|
22 |
FOR XML PATH(''), TYPE) value('.', |
'nvarchar(max)'), |
||
23 |
1, 2 ''); |
|
|
|
24 |
|
|
|
|
25IF (LEN @bad records) > 0
26BEGIN
27SET @msg = CONCAT('The following readers have more books
28 |
than allowed (10 allowed) :', @bad records ) ; |
29 |
RAISERROR @msg 16 1); |
30ROLLBACK TRANSACTION;
31RETURN;
32END;
33
34SET IDENTITY INSERT [subscriptions] ON;
35INSERT INTO [subscriptions]
36 |
([sb id], |
37 |
[sb subscriber], |
38 |
[sb book] |
39 |
[sb start], |
40 |
[sb finish], |
41 |
[sb is active]) |
42 |
SELECT ( CASE |
43 |
WHEN [sb id] IS NULL |
44 |
OR [sb id] = 0 THEN IDENT CURRENT('subscriptions') |
45 |
+ IDENT INCR('subscriptions') |
46 |
+ ROW NUMBER() OVER (ORDER BY |
47 |
(SELECT 1 ) |
48 |
- 1 |
49 |
ELSE [sb id] |
50 |
END ) AS [sb id], |
51[sb subscriber],
52[sb book],
53[sb start]
54[sb finish],
55[sb is active]
56 FROM [inserted]
57SET IDENTITY INSERT [subscriptions] OFF;
58GO
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 352/545
Пример 33: контроль операций модификации данных
Исследование 4.2.1.EXP.B. Проведём исследование на базе данных «Большая библиотека», сравнив скорость работы представленных неправильного и правильного решений для MS SQL Server.
После выполнения тысячи запросов на вставку данных, нарушающих условие задачи, медианы времени (в секундах) приняли следующие значения:
Неправильное решение |
Правильное решение |
Разница, раз |
6.598 |
2.746 |
2.4 |
Получилось не так внушительно, как в случае с MySQL, но всё равно достаточно, чтобы ощутить разницу в производительности неправильного и правильного вариантов решения.
Переходим к решению для Oracle, в котором также представим два варианта
— неправильный и правильный. Их внутренняя логика будет полностью эквивалентна решению данной задачи для MySQL, потому сразу переходим к коду.
Oracl |
і |
Решение 4.2.1.b (неправильное решение) |
| |
|
||
e |
|
|||||
|
|
|
|
|
|
|
1 |
CREATE TRIGGER "sbs ctr 10 bks ins upd WRONG" |
|
||||
2 |
AFTER INSERT OR UPDATE |
|
|
|
|
|
3 |
ON "subscriptions" |
|
|
|
|
|
4 |
FOR EACH ROW |
|
|
|
|
|
5 |
|
DECLARE |
|
|
|
|
6 |
|
PRAGMA AUTONOMOUS TRANSACTION; |
|
|
||
7 |
|
msg NCLOB; |
|
|
|
|
8 |
|
BEGIN |
|
|
|
|
9 |
|
SELECT NVL((SELECT UTL RAW.CAST TO NVARCHAR2 |
|
|||
10 |
|
( |
|
|
|
|
11 |
|
LISTAGG |
|
|
||
12 |
|
( |
|
|
|
|
13 |
|
|
UTL RAW.CAST TO RAW(N'(id=' |
|| |
||
14 |
|
|
"sb subscriber" || N', ' || "s name" || |
|||
15 |
|
|
N', books=' || "s books" || N')'), |
|||
16 |
|
|
UTL RAW.CAST TO RAW(N', ') |
|
||
17 |
|
) |
|
|
|
|
18 |
|
WITHIN GROUP (ORDER BY "sb subscriber"! |
||||
19 |
|
) |
|
|
|
|
20 |
|
FROM |
(SELECT "sb subscriber", |
|
||
21 |
|
|
|
"s name", |
|
|
22 |
|
|
|
COUNT("sb book") AS "s books" |
||
23 |
|
|
FROM |
"subscribers" |
|
|
24 |
|
|
|
JOIN "subscriptions" |
|
|
25 |
|
|
|
ON "s id" = "sb subscriber" |
||
26 |
|
WHERE |
"sb is active" = 'Y' |
|
||
27 |
|
GROUP |
BY "sb subscriber", "s name" |
|||
28 |
|
HAVING COUNT("sb book") > 10 |
"prepared data"), |
|||
29 |
|
|
|
|
'') |
|
30 |
|
INTO msg FROM dual; |
|
|
|
|
31 |
|
|
|
|
|
|
32 |
|
IF (LENGTH msg) > 0 |
|
|
|
|
33 |
|
THEN |
|
|
|
|
34 |
|
RAISE APPLICATION ERROR(-20001 |
'The following readers have |
|||
35 |
|
|
|
|
more books than allowed |
|
36 |
|
|
|
|
(10 allowed): ' || msg); |
|
37 |
|
END IF; |
|
|
|
|
38 |
|
END; |
|
|
|
|
Поскольку Oracle запрещает обращение из триггера к таблице subscriptions, в которую производится вставка, мы запускаем выполнение триггера в автономной транзакции (строка 6). Эту особенность придётся учитывать и в правильном решении, которое мы сейчас и рассмотрим.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 353/545
Пример 33: контроль операций модификации данных |
|
|
|||
Oracl |
і Решение 4.2.1.b (триггеры для таблицы subscriptions) |
| |
|
||
e |
|
||||
|
|
|
|
|
|
1 |
CREATE TRIGGER "sbs ctr 10 bks ins upd OK" |
|
|
||
2 |
BEFORE INSERT OR UPDATE |
|
|
|
|
3 |
ON "subscriptions" |
|
|
|
|
4 |
FOR EACH ROW |
|
|
|
|
5 |
DECLARE |
|
|
|
|
6 |
PRAGMA AUTONOMOUS TRANSACTION; |
|
|
|
|
7 |
msg NCLOB; |
|
|
|
|
8 |
BEGIN |
|
|
|
|
9 |
SELECT NVL((SELECT (N'Subscriber ' || "s name" || N' (id=' || |
||||
10 |
"sb subscriber" || N') already has ' || |
||||
11 |
"sb books" || N' books out of 10 allowed.') |
||||
12 |
AS "message" |
|
|
|
|
13 |
FROM |
(SELECT "sb subscriber", |
|||
14 |
|
|
COUNT("sb book") AS "sb books" |
||
15 |
|
FROM |
"subscriptions" |
|
|
16 |
|
WHERE |
"sb is active" = 'Y' |
||
17 |
|
AND "sb subscriber" = |
new "sb subscriber" |
||
18 |
|
GROUP |
BY "sb subscriber" |
||
19 |
|
HAVING COUNT("sb book") >= 10) |
|||
20 |
|
"prepared_data" |
|
||
21 |
JOIN "subscribers" |
|
|
||
22 |
ON "sb subscriber" = "s id" , |
|
|||
23 |
'') |
|
|
|
|
24 |
INTO msg FROM dual; |
|
|
|
|
25 |
|
|
|
|
|
26 |
IF (LENGTH msg) > 0 |
|
|
|
|
27 |
THEN |
|
|
|
|
28 |
RAISE APPLICATION ERROR(-20001 |
msg); |
|
|
|
29 |
END IF; |
|
|
|
|
30 |
END; |
|
|
|
|
Остаётся проверить разницу в скорости работы представленных решений.
Исследование 4.2.1.EXP.C. Проведём исследование на базе данных «Большая библиотека», сравнив скорость работы представленных неправильного и правильного решений для Oracle.
После выполнения тысячи запросов на вставку данных, нарушающих условие задачи, медианы времени (в секундах) приняли следующие значения:
Неправильное решение |
Правильное решение |
|
Разница, раз |
|||
99.667 |
|
|
4.961 |
|
20 |
|
Если свести все результаты исследований в одну таблицу, получается: |
||||||
СУБД |
Неправильное |
|
Правильное |
Разница, раз |
||
|
решение |
|
решение |
|
||
MySQL |
51.177 |
|
0.009 |
|
5686 |
|
MS SQL Server |
6.598 |
|
2.746 |
|
2.4 |
|
Oracle |
99.667 |
|
4.961 |
|
20 |
Также отметим, что во всех СУБД в неправильном варианте решения в процессе накопления информации обо всех читателях, для которых нарушается условие задачи, мы рискуем превысить максимально допустимый размер строки, аккумулирующей данную информацию. Так что второй (правильный) вариант решения оказывается не только быстрее, но и надёжнее.
На этом решение данной задачи завершено.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 354/545
Пример 33: контроль операций модификации данных
Решение 4.2.1.c{315}.
Поскольку по условию задачи запрещено только изменение значения поля sb_is_active с N на Y у уже существующих записей, нам понадобится только
UPDATE-триггер.
Задачи такого типа очень просто и удобно решаются с использованием триггеров уровня записи (поддерживаются MySQL и Oracle) — мы используем ключевые слова old и new для доступа к старому и новому значению поля sb_is_active.
В случае с триггерами уровня выражения (только такие триггеры есть в MS SQL Server) придётся использовать запрос не объединение из псевдотаблиц inserted и deleted, чтобы найти взаимное соответствие старого и нового значений поля sb_is_active для каждой записи. Также придётся запретить изменение значения первичного ключа (строки 8-14 кода триггера для MS SQL Server), чтобы иметь возможность гарантированно получать соответствие старого и нового значения контролируемого поля.
В остальном логика решения этой задачи тривиальна: если происходит попытка изменения данных запрещённым по условию задачи образом, мы выводим сообщение об ошибке и «откатываем транзакцию».
Традиционно начнём с кода для MySQL.
MySQL і Решение 4.2.1.С (триггер для таблицы subscriptions)
1 DELIMITER $$
2
3CREATE TRIGGER 'sbs cntrl is active'
4BEFORE UPDATE
5ON 'subscriptions'
6FOR EACH ROW
7BEGIN
8IF ((OLD.'sb is active' = 'N') AND (NEW.'sb is active' = 'Y'))
9THEN
10SET @msg = CONCAT('It is prohibited to activate previously
11 |
deactivated subscriptions (rule violated |
|
12 |
for subscription with id ', NEW.'sb id', |
').'); |
13SIGNAL SQLSTATE '45001' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1001;
14END IF;
15END;
16$$
17
18DELIMITER ;
Врешении для MS SQL Server можно было бы пойти по более оптимальному
сточки зрения производительности пути и сделать INSTEAD OF триггер, но в таком
случае код триггера стал бы сложнее.
Поскольку в решении{328} задачи 4.2.1.b{315} мы уже рассматривали такую ситуацию, здесь мы пожертвуем производительностью ради краткости и понятности кода самого триггера.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 355/545
Пример 33: контроль операций модификации данных
MS SQL I |
Решение 4.2.1.c (триггер для таблицы subscriptions) |
| |
1CREATE TRIGGER [sbs cntrl is active]
2ON [subscriptions]
3AFTER UPDATE
4AS
5DECLARE @bad records NVARCHAR(max);
6DECLARE @msg NVARCHAR(max);
7 |
|
8 |
IF (UPDATE([sb id])) |
9 |
BEGIN |
10 |
RAISERROR ('Please, do NOT update surrogate PK |
11 |
on table [subscriptions]!', 16. 1); |
12ROLLBACK TRANSACTION;
13RETURN;
14END;
15 |
|
|
16 |
SELECT @bad records = STUFF((SELECT ', ' + |
|
17 |
|
CASTl[inserted] [sb id] AS NVARCHAR |
18 |
FROM |
[deleted] |
19 |
|
JOIN [inserted] |
20 |
|
ON [deleted] [sb id] = |
21 |
|
[inserted] [sb id] |
22 |
WHERE |
[deleted] [sb is active] = 'N' |
23 |
|
AND [inserted] [sb is active] = 'Y' |
24 |
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), |
|
25 |
1, 2 ''); |
|
26 |
|
|
27 |
IF (LEN @bad records 1 > 0 |
|
28BEGIN
29SET @msg = CONCAT('It is prohibited to activate previously
30 |
deactivated subscriptions (rule violated for |
31 |
subscriptions with id ', @bad records, ').'); |
32 |
RAISERROR @msg 16 1); |
33 |
ROLLBACK TRANSACTION; |
34 |
RETURN; |
35 |
END; |
36 |
GO |
И, наконец, представим решение для Oracle. Оно отличается от решения для MySQL только синтаксически, т.к. сама логика этих двух решений полностью идентична.
Oracle |
Решение 4.2.1 .с (триггер для таблицы |
1CREATEsubscriptions)TRIGGER "sbs ctr is active"
2BEFORE UPDATE
3ON "subscriptions"
4FOR EACH ROW
5BEGIN
6IF (( old "sb is active" = 'N') AND ( new "sb is active" = 'Y'))
7THEN
8 |
RAISE APPLICATION ERROR(-20001 'It is prohibited to activate |
9 |
previously deactivated subscriptions |
10 |
(rule violated for subscription with |
11 |
id ' || :new "sb id" || ').'); |
12END IF;
13END;
На этом решение данной задачи завершено. Проверить его работоспособность вы можете сами, выполняя запросы на обновление данных в таблице subscriptions так, чтобы либо не нарушить, либо нарушить условие задачи.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 356/545
Пример 33: контроль операций модификации данных
Задание 4.2.1.TSK.A: создать триггер, не позволяющий добавить в базу &данных информацию о выдаче книги, если выполняется хотя бы одно из
условий:
•дата выдачи или возврата приходится на воскресенье;
•читатель брал за последние полгода более 100 книг;
•промежуток времени между датами выдачи и возврата менее трёх дней.
Задание 4.2.1.TSK.B: создать триггер, не позволяющий выдать книгу читателю, у которого на руках находится пять и более книг, при условии, что суммарное время, оставшееся до возврата всех выданных ему книг, составляет менее одного месяца.
Задание 4.2.1.TSK.C: переработать решение{335} задачи 4.2.1.c{315} для MS SQL Server, изменив AFTER-триггер на INSTEAD OF триггер.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 357/545
Пример 34: контроль формата и значений данных
4.2.2.Пример 34: контроль формата и значений данных
ОЗадача 4.2.2.a{338}: создать триггер, допускающий регистрацию в библиотеке только таких читателей, имя которых содержит хотя бы два слова и одну точку.
ОЗадача 4.2.2.b{342}: создать триггер, допускающий регистрацию в библиотеке только книг, изданных не более ста лет назад.
Ожидаемый результат 4.2.2.a:
При попытке внести в базу данных изменения, противоречащие условию задачи, операция (транзакция) должна быть отменена. Также должно быть выведено сообщение об ошибке, наглядно поясняющее суть проблемы, например: «Subscribers name should contain at least two words and one point, but the following name violates this rule: ИвановИИ».
Ожидаемый результат 4.2.2.b:
При попытке внести в базу данных изменения, противоречащие условию задачи, операция (транзакция) должна быть отменена. Также должно быть выведено сообщение об ошибке, наглядно поясняющее суть проблемы, например: «The following issuing year is more than 100 years in the past: 1812».
•4 Решение 4.2.2.a{338}:
В решении{328} задачи 4.2.1.b{315} мы уже рассматривали подробно преимущества BEFORE- и INSTEAD OF триггеров перед AFTER-триггерами в плане произво-
дительности, потому здесь не будем повторно приводить те же самые рассуждения, а сразу переходим к сути задачи.
Самое сложное здесь — посчитать слова. И сложность эта — не столько техническая, сколько «философская»: что считать словом? Договоримся, что словом мы будем считать непрерывную последовательность из букв и знаков - (минус) и ' (апостроф). Приняв это допущение, мы можем построить универсальное решение на основе регулярных выражений (для СУБД, которые их поддерживают).
Изучение сути регулярных выражений выходит за рамки этой книги, потому
— вот готовый универсальный вариант, который должен сработать в подавляющем большинстве СУБД и языков программирования (да, можно написать более оптимальный и элегантный вариант, но это повышает риск потери универсальности):
Л[а-иА-2а-яА-ЯёЁ\'-]+([Ла-иА-2а-яА-ЯёЁ\'-]+[а-иА-2а-яА-ЯёЁ\'.-]+){1,}$
Графическое представление этого регулярного выражения представлено на рисунке 4.a. Буквы «ё» добавлены в символьный класс как отдельный символ потому, что они не входят в диапазон букв русского алфавита.
Если по какой-то причине вы не хотите или не можете использовать регулярные выражения, есть второй способ убедиться, что в строке есть два слова (если допустить, что разделителем слов является пробел): нужно подсчитать количество пробелов в строке, у которой гарантированно удалены т.н. «концевые пробелы» в начале и конце. Если полученное число больше ноля, в строке точно есть как минимум два слова.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 358/545
Пример 34: контроль формата и значений данных
Рисунок 4.a — Графическое представление регулярного выражения14
Традиционно начинаем с MySQL. И сразу же сталкиваемся с проблемой: эта СУБД не поддерживает мультибайтовые строки при использовании регулярных выражений14 15, а потому нам придётся преобразовывать анализируемые значения к однобайтовой кодировке (например, CP1251). Для русского алфавита это безопасно, но для других языков может привести к искажениям данных и неверной работе механизма регулярных выражений.
MySQL I |
Решение 4.2.2.a (триггеры для таблицы subscribers) |
| |
1 DELIMITER $$
2
3CREATE TRIGGER 'sbsrs cntrl name ins'
4BEFORE INSERT
5ON 'subscribers'
6FOR EACH ROW
7BEGIN
8IF ((CAST(NEW.'s name' AS CHAR CHARACTER SET cp1251) REGEXP
9CAST('Л[a-zA-Za-яА-ЯёЁХ'-] + ([лa-zA-Zа-яА-ЯёЁ\'-]+[a-zA-Za-яА-
10ЯёЁ\'.-]+){1,}$' AS CHAR CHARACTER SET cp1251 ) = 0)
11OR (LOCATE('.', NEW.'s name') = 0
12THEN
13 |
SET @msg = CONCAT('Subscribers name |
should contain at |
|
14 |
least two words and onepoint, but |
the following |
|
15 |
name violates this rule ', NEW. 's name'); |
||
16 |
SIGNAL SQLSTATE '45001' SET MESSAGE |
TEXT = @msg MYSQL |
ERRNO = 1001 |
17END IF;
18END;
19$$
14https://regexper.com/#'“[a-zA-Z%D0%B0-%D1%8F%D0%90-%D0%AF%D1%91%D0%81\%27-]%2B%28['“a-zA-Z%D0%B0- %D1 %8F%D0%90-%D0%AF%D1 %91 %D0%81 \%27-]%2B[a-zA-Z%D0%B0-%D1 %8F%D0%90- %D0%AF%D1 %91 %D0%81\%27-]%2B%29{1 %2C}%24
15http://dev.mysql.Com/doc/refman/5.6/en/regexp.html#operator_regexp
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 359/545