Система управления базами данных SQLite. Изучаем язык запросов SQL и реляционные базы данных на примере библиотекой SQLite3. Курс для начинающих.
Часть 11.5: Каскадная модификации данных в базах данных SQLite3
Привет, посетитель сайта ZametkiNaPolyah.ru! Продолжаем изучать базы данных и наше знакомство с библиотекой SQLite3. На своём блоге я уже довольно много писал про обеспечение целостности данных в реляционных базах данных и для примера мы используем замечательную встраиваемую библиотеку SQLite. Замечу, что многое из того, что написано актуально и для других серверов баз данных. Впрочем, с темой поддержания целостности данных мы почти закончили. Перейдем к рассмотрению каскадных операций манипуляции данными в SQLite. В базах данных SQLite таких операции реализовано две: каскадная модификация данных и каскадное удаление данных. Из этой публикации вы узнаете про каскадную модификацию данных в базах данных под управлением SQLite3.
Каскадная модификации данных в базах данных SQLite3
Каскадная модификация данных позволяет избежать нарушения правила внешнего ключа, с помощью которого мы реализуем связи между таблицами в базе данных. Помимо этого, каскадная модификация уменьшает количество SQL запросов, необходимых для внесения изменения значений, которые хранятся в связанных таблицах. Сперва мы поговорим о реализации каскадной модификации данных в SQLite3, а затем рассмотрим пример каскадной модификации данных в базах данных.
Каскадная модификация данных в базе данных SQLite. ON UPDATE в SQLite3
Обеспечение целостности данных – очень важная тема при рассмотрение любой СУБД, когда мы рассматривали ограничения уровня таблица, а точнее внешние ключи в базах данных SQLite3, то столкнулись с тем, что для модификации данных таблицы со ссылкой при помощи команды UPDATE, нам было необходимо сперва изменить данные в таблице-справочнике, а уже затем изменять данные в таблице, которая ссылается на справочник.
С этой проблемой нам поможет справиться каскадная модификация данных. Каскадная модификация данных в базах данных SQLite реализована при помощи ON UPDATE. Ключевая фраза ON UPDATE позволяет нам избавиться от сложностей модификации таблиц в базе данных, которые связаны между собой внешним ключом FOREIGN KEY.
Как понимать фразу каскадная модификация? Всё дело в том, что ключевая фраза ON UPDATE CASCADE, которая указывается после FOREIGEN KEY говорит СУБД о том, что та должна модифицировать обе таблицы друг за другом – каскадно. Таким образом, указав SQLite, что данные должны модфицироваться каскадом, мы сможем написать только один SQL запрос UPDATE, а SQLite сама обновит обе таблицы.
Каскадную модификацию ON UPDATE лучше показать на примере, так будете доступнее, понятнее и нагляднее.
Примеры каскадной модификации данных в базе данных SQLite. ON UPDATE в SQLite3
Давайте посмотрим пример каскадной модификации ON UPDATE в базах данных SQLite. Для этого создадим две таблицы, связанных внешним ключом, при помощи команды CREATE.
Система управления базами данных SQLite. Изучаем язык запросов SQL и реляционные базы данных на примере библиотекой SQLite3. Курс для начинающих.
Часть 11.6: Каскадное удаления данных в базах данных SQLite
Привет, посетитель сайта ZametkiNaPolyah.ru! Продолжаем изучать базы данных и наше знакомство с библиотекой SQLite3. Прошлая публикация была про каскадную модификацию, в этой мы с вами поговорим про каскадное удаление данных из базы данных SQLite3. Каскадное удаление данных это способ удалить данные из связанных таблиц и при этом не нарушить ограничение внешнего ключа, кроме того, каскадное удаление данных упрощает нам работу при удалении строк в связанных таблицах.
Каскадное удаления данных в базах данных SQLite
Из этой записи вы узнаете для чего нужно каскадное удаление данных в реляционных базах данных и, как каскадное удаление облегчает жизнь разработчику. И увидите пример работы каскадного удаления данных из базы данных под управлением SQLite в таблицах, связанных связью многие ко многим, в общем, увидите в действие конструкцию ON DELETE CASCADE.
Каскадное удаление данных из базы данных SQLite
Мы уже рассмотрели всевозможные способы обеспечения целостности данных в базах данных под управлением SQLite. Успели разобраться с ограничениями уровня столбца и ограничениями уровня таблицы и чуть ранее рассмотрели каскадное обновление данных. Теперь перейдем к каскадному удалению данных в базах данных SQLite3.
При реализации связей между таблицами при помощи внешнего ключа или FOREIGN KEY мы сталкиваемся с такой проблемой: для удаления данных из таблиц базы данных нам необходимо выполнить две команды DELETE: сперва нужно удалить данные из таблицы справочника, а затем удалить строку из таблицы, которая ссылается на справочник. В противном случае SQLite скажет, что было нарушено ограничение внешнего ключа и не даст удалить данные.
Чтобы избежать такой проблемы и не писать лишние SQL запросы в SQLite было реализовано каскадное удаление данных из таблиц, при каскадном удаление данных мы не пишем лишний SQL запрос DELETE, а также гарантируем себе то, что целостность данных в базах данных не будет нарушена.
Операция каскадного удаления данных реализуется при помощи ключевой фразы ON DELETE CASCADE. Фраза ON DELETE CASCADE говорит SQLite о том, что сперва нужно удалить данные из справочника или домена данных, а затем данные нужно удалять из таблицы, которая ссылается на этот справочник.
Чтобы реализовать каскадное удаление данных из таблицы базы данных, нужно записать правило ON DELETE сразу после FOREIGN KEY, тогда SQLite начнет «понимать», что при удалении данных из справочника, ей нужно удалять и данные из связанных таблиц.
Давайте реализуем пример связи многие ко многим и посмотрим, как происходит каскадное удаление данных из базы данных SQLite3 и поймем, как работает конструкция ON DELETE CASCADE.
Примеры каскадного удаления данных из базе данных SQLite. ON DELETE в SQLite3
Чтобы посмотреть, как удалить данные каскадом, давайте создадим три таблицы в базе данных, которые будут связаны связью многие ко многим, воспользуемся командой CREATE:
Руководство по проектированию реляционных баз данных. Каскадное удаление данных
Дополнение к циклу переведенных статей.
Статьи: 1-3, 4-6, 7-9, 10-13, 14-15
Информация в статье относится к 5-й части руководства.
В комментариях один из пользователей небеспричинно упрекнул в отсутствии информации о каскадном удалении данных. Восполняю пробел. У автора статей нет информации на эту тему, поэтому я написал небольшую статью об этом. Она достаточно логично впишется в указанный цикл.
Для начала, чтобы не было путаницы, стоит сказать, что речь не столько и не только о каскадном удалении данных, а о теме ссылочной целостности и внешних ключах, частью которой и является каскадное удаление данных.
Введение.
Ближе к сути.
О внешних ключах было рассказано в переводах, останавливаться не буду на этом. Расскажу о “спутнике”.
В случае, если не знать теории, следить за связями данных придется самостоятельно. Альтернативным вариантом является возложение этой задачи на базу данных. Что же за, так названное, слежение за связями данных? Чтобы понять, нужен пример.
У нас есть какие-то вещи. Они разбросаны, их много. Мы хотим навести порядок. Порядок – это, зачастую, классификация (категоризация) и опись. Мы хотим порядка, при этом, мы умеем работать с базами данных и не хотим ничего писать на бумаге. Мы записываем все вещи “в столбик”. Далее мы просматриваем список и определяем категории к которым относятся вещи.
Пусть это часть наших вещей, остальные не рассматриваем:
Книга 1, книга 2, книга 3 – это книги, как ни странно.
Компьютерная мышка, клавиатура – это компьютерная периферия.
Ручка, степлер – это канцелярские принадлежности.
Мы создаем две таблицы в базе данных: categories (категории) и stuff(вещи).
1 | книги
2 | компьютерная периферия
3 | канцелярские принадлежности
stuff_id | category_id | name
1 | 1 | книга 1
2 | 1 | книга 2
3 | 1 | книга 3
4 | 2 | компьютерная мышка
5 | 2 | клавиатура
6 | 3 | ручка
7 | 3 | степлер
P.S. Изображения с habrastorage.org не отображаются.
Итого: у нас есть книги, компьютерная периферия, канцелярские принадлежности.
Мы захотели выкинуть или подарить все наши книги, не хотим видеть эти вещи, как категорию, у себя дома, нам нравятся электронные книги. Мы удаляем из таблицы категорий категорию “книги”. При этом, у нас остаются вещи из этой категории в другой таблице, мы ссылаемся на эти категории в таблице вещей. Это и называется нарушением ссылочной целостности. Казалось бы, нет у нас категории, а значит и нет книг, но записи в таблице вещей остались и вещей-то у нас много и в будущем положение дел может повториться и повторится и тогда у нас будет бардак, много лишней информации и все вытекающие последствия как в удобстве работы с нашей информацией, так и в технической части при работе с базой (напр., поиск информации). И тут приходит понимание, что нам нужно работать с двумя таблицами, следить в каких случаях связи могут быть нарушены, сломаны и совершать какие-то телодвижения и тут есть два варианта: самостоятельно делаем это или, вот тут знание – сила, мы может переложить эту головную боль на базу данных.
В рамках реляционной модели данных таблица категорий является предком, а таблица вещей – потомком. Тут все понятно, как родитель и ребенок. Более того, случаи, в которых связи могут быть сломаны, также определены (берем и пользуемся). Наш случай – не единственный.
Сломаться связи могут (если говорить “правильным” языком – ссылочная целостность может нарушиться) в следующих случаях:
Средства поддержания ссылочной целостности SQL (скажу сразу, наперед, когда будет нужно – поймете; если говорить про РСУБД MySQL, то использование этих средств вместе с внешними ключами возможно только для таблиц InnoDB; внешние ключи можно искользовать в MyISAM, создавая определенную структуру даных, но тогда вся головная боль по слежению за связями ложится на пользователя) позволяют обрабатывать указанные случаи.
И вот как решаются эти проблемы (в порядке перечисления):
Теперь два последних. Тут положение дел более интересное.
Где необязательные конструкции ON DELETE и ON UPDATE позволяют задать те самые варианты решения проблемы, которые рассмотрены выше. А эти ключевые слова именуют их:
CASCADE – при удалении или обновлении записи в таблице-предке, которая содержит первичный ключ, автоматически удаляются или обновляются записи со ссылками на это значение в таблице-потомке. В нашем случае, если мы удалим категорию, то удалятся и все вещи, относящиеся к этой категории в таблице вещей. Если мы обновим идентификатор у категории, то у вещей, которые ссылались на эту категорию, идентификатор также изменится на новый.
То самое, каскадное, но, как видите, не только удаление.
SET NULL – при удалении или обновлении записи в таблице-предке, которая содержит первичный ключ, значения внешнего ключа в таблице-потомке устанавливаются в NULL.
В нашем случае, если мы удалим или обновим идентификатор категории в таблице категорий, то у всех вещей, которые ссылались, относились, к данной категории в поле с идентификатором категории будет выставлено NULL.
NO ACTION — при удалении или обновлении записи в таблице-предке, которая содержит первичный ключ, в таблице-потомке никаких действий предприниматься не будет.
В нашем случае, если мы удалим или обновим идентификатор категории в таблице категорий, то это никак не повлияет на таблицу вещей.
RESTRICT – если в таблице-потомке есть записи, которые ссылаются на существующий первичный ключ в таблице-потомке, то при удалении или обновлении записи с первичным ключом в таблице-предке возвратится ошибка.
В нашем случае, если мы попробуем обновить или изменить идентификатор категории при том, что есть вещи, относящиеся к этой категории, то мы получим ошибку.
SET DEFAULT – тут понятно из названия, что при удалении или обновлении записи в таблице-предке, которая содержит первичный ключ, в таблице-потомке соответствующим записям будет выставлено значение по умолчанию. Есть одно “НО”. В РСУБД MySQL это ключевое слово не используется.
А теперь вновь – к каскадному удалению данных. Почему именно оно на слуху? Почему спросили про него в первую очередь, не смотря на то, что оно лишь одно из. Наверное, потому, что каскадное удаление данных наиболее частое решение проблемы.
Каскадное удаление
В Entity Framework Core (EF Core) связи представлены с помощью внешних ключей. Сущность с внешним ключом является дочерней или зависимой в связи. Значение внешнего ключа этой сущности должно соответствовать значению первичного ключа (или альтернативному значению ключа) связанной основной или родительской сущности.
Если основная или родительская сущность удалена, значения внешних ключей зависимых и дочерних сущностей больше не соответствуют первичному или альтернативному ключу какой-либо основной или родительской сущности. Это недопустимое состояние, которое в большинстве баз данных приведет к нарушению ссылочного ограничения.
Избежать этого нарушения можно двумя способами.
Первый способ подходит только для необязательных связей, в которых свойство внешнего ключа (и столбец базы данных, с которым оно сопоставлено), должно допускать значения NULL.
Второй вариант допустим для любого типа связи и называется каскадным удалением.
В этом документе описывается каскадное удаление (и удаление потерянных объектов) в контексте обновления базы данных. При этом активно используются концепции, представленные в разделах Отслеживание изменений в EF Core и Изменение внешних ключей и свойств навигации. Прежде чем приступать к изучению этого документа, обязательно ознакомьтесь с ними.
Вы можете запустить и отладить весь код, используемый в этой документации, скачав пример кода из GitHub.
Когда происходит каскадная реакция на события
Каскадное удаление требуется в тех случаях, когда зависимая или дочерняя сущность теряет связь со своей текущей основной или родительской сущностью. Это может произойти в результате удаления основной или родительской сущности, а также в случае разрыва связи между ней и зависимой или дочерней сущностью.
Удаление основной или родительской сущности
По соглашению эта связь настроена как обязательная, поскольку свойство внешнего ключа Post.BlogId не допускает значения NULL. Для обязательных связей по умолчанию используется каскадное удаление. Дополнительные сведения о моделировании связей см. в разделе Связи.
При удалении блога происходит каскадное удаление всех его записей. Пример:
Метод SaveChanges создает следующий код SQL (на примере SQL Server).
Разрыв связи
Вместо удаления блога можно разорвать связь между ним и каждой его записью. Это можно сделать, установив значение NULL для свойства навигации по ссылке Post.Blog для каждой записи:
В обоих случаях будет достигнут одинаковый результат, то есть записи, которые больше не связаны ни с одним блогом, удаляются, а сам блог остается.
Удаление сущностей, которые больше не связаны ни с одной основной или родительской сущностью, называется удалением потерянных объектов.
Концепции каскадного удаления и удаления потерянных объектов тесно взаимосвязаны. В обоих случаях при разрыве связи с основной или родительской сущностью ее зависимые и дочерние сущности удаляются. При каскадном удалении это происходит вследствие удаления самой основной или родительской сущности. При удалении потерянных объектов основная или родительская сущность по-прежнему существует, но при этом больше не связана с зависимыми и дочерними сущностями.
Где происходит каскадная реакция на события
Каскадная реакция на события может применяться к следующим сущностям.
Каскадное удаление отслеживаемых сущностей
EF Core всегда применяет к отслеживаемым сущностям каскадную реакцию на события. Это значит, что, если приложение загружает все соответствующие зависимые или дочерние сущности в DbContext (как показано в приведенных выше примерах), каскадная реакция на события будет корректно применяться, независимо конфигурации базы данных.
Точное время, когда происходят каскадные реакции для отслеживаемых сущностей, можно контролировать с помощью ChangeTracker.CascadeDeleteTiming и ChangeTracker.DeleteOrphansTiming. Дополнительные сведения см. в разделе Изменение внешних ключей и свойств навигации.
Каскадное удаление в базе данных
Многие системы баз данных также реализуют каскадную реакцию на события, которая активируется при удалении сущности в базе данных. EF Core определяет конфигурацию такой реакции на основе настроек каскадного удаления в модели EF Core при создании базы данных с использованием метода EnsureCreated или миграций EF Core. Например, в представленной выше модели при использовании SQL Server для записей создается следующая таблица.
Если мы знаем, что база данных настроена таким образом, можно удалить блог без предварительной загрузки записей. В этом случае база данных автоматически удалит все связанные с ним записи. Пример:
Как правило, базы данных не поддерживают автоматическое удаление потерянных объектов. Это связано с тем, что в базах данных используются только внешние ключи и нет свойств навигации, тогда как в EF Core связи представляются с использованием и внешних ключей, и свойств навигации. Это значит, что в большинстве случаев для разрыва связи обе ее стороны должны быть загружены в DbContext.
Выполняющаяся в памяти база данных EF Core в настоящее время не поддерживает каскадное удаление в базе данных.
Не следует настраивать каскадное удаление в базе данных при обратимом удалении сущностей. Это может привести к случайному полному (вместо обратимого) удалению сущностей.
Ограничения для каскадной реакции на события в базе данных
Некоторые базы данных, особенно SQL Server, устанавливают ограничения для каскадных операций, которые приводят к возникновению циклов. Рассмотрим следующую модель.
В этой модели устанавливаются три связи, каждая из которых является обязательной и по соглашению настраивается для каскадного удаления.
Это вполне разумный, хотя и достаточно строгий подход к управлению блогом. Тем не менее при попытке создать базу данных SQL Server с этими настройками каскадной реакции на событий возникнет следующее исключение:
Microsoft.Data.SqlClient.SqlException (0x80131904): Введение ограничения внешнего ключа (FOREIGN KEY) FK_Posts_Person_AuthorId для таблицы Posts может привести к появлению циклов или множественных каскадных путей. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION либо измените другие ограничения внешнего ключа (FOREIGN KEY).
Эту проблему можно решить двумя способами.
Применение первого подхода в нашем примере позволяет сделать связь между блогом и его владельцем необязательной, предоставив свойство внешнего ключа, допускающее значение NULL.
Поскольку эта связь не является обязательной, блог может существовать без владельца, то есть каскадное удаление больше не будет настроено по умолчанию. Это значит, что каскадная реакция на события больше не будет приводить к возникновению циклов, благодаря чему база данных может быть создана в SQL Server без ошибок.
Если использовать второй подход, связь между блогом и владельцем можно оставить обязательной и настроить для каскадного удаления, но при этом задать применение этой конфигурации только к отслеживаемым сущностям, а не к базе данных.
Что произойдет, если загрузить владельца блога и сам блог, а затем удалить владельца?
EF Core выполнит каскадное удаление владельца и принадлежащего ему блога.
Тем не менее, если блог не был загружен, при удалении его владельца происходит следующее.
После этого возникает исключение из-за нарушения ограничения внешнего ключа в базе данных.
Microsoft.Data.SqlClient.SqlException: Конфликт инструкции DELETE с ограничением REFERENCE FK_Blogs_People_OwnerId. Конфликт произошел в базе данных Scratch, таблица dbo.Blogs, столбец OwnerId. Выполнение данной инструкции было прервано.
Каскадное распространение значений NULL
Для необязательных связей свойства внешнего ключа, допускающие значения NULL, сопоставлены со столбцами базы данных, также допускающими такие значения. Это означает, что внешнему ключу может быть присвоено значение NULL, если текущая основная или родительская сущность удаляется или разрывается ее связь с зависимой или дочерней сущностью.
Еще раз возьмем примеры из раздела Когда происходит каскадная реакция на события. На этот раз рассмотрим необязательную связь, которая представлена допускающим значение NULL свойством внешнего ключа :
Этому свойству внешнего ключа будет присваиваться значение NULL для каждой записи при удалении связанного с ней блога. Например, следующий код аналогичен приведенному ранее.
Тем не менее теперь при вызове метода SaveChanges будут выполнены следующие обновления базы данных.
Аналогичным образом, при разрыве связи в любом из приведенных выше примеров:
При вызове метода SaveChanges записи будут обновлены с присвоением внешнему ключу значения NULL.
Дополнительные сведения о том, как EF Core управляет внешними ключами и свойствами навигации при изменении их значений, см. в разделе Изменение внешних ключей и свойств навигации.
По умолчанию, начиная с первой версии Entity Framework 2008 года, выполнялась адресная привязка таких связей. До появления EF Core у такого подхода не было своего названия и его нельзя было изменить. Сейчас он называется ClientSetNull и описывается в следующем разделе.
Базы данных также можно настроить для подобного каскадного распространения значений NULL при удалении основной или родительской сущности в необязательной связи. Тем не менее этот подход применяется гораздо реже операций каскадного удаления в базе данных. Одновременное выполнение каскадного удаления и каскадного распространения значений NULL в базе данных SQL Server почти всегда будет приводить к возникновению циклов связей. Дополнительные сведения о настройке каскадного распространения значений NULL см. в следующем разделе.
Настройка каскадной реакции на события
Прежде чем переходить к этому разделу, ознакомьтесь с приведенными выше. Для понимания параметров конфигурации потребуется знание представленных ранее материалов.
Настройка каскадной реакции на события для отдельных связей осуществляется с помощью метода OnDelete в OnModelCreating. Пример:
Дополнительные сведения о настройке связей между типами сущностей см. в разделе Связи.
Влияние на схему базы данных
| Значение перечисления DeleteBehavior | Влияние на схему базы данных |
|---|---|
| Cascade | ON DELETE CASCADE |
| Ограничение | ON DELETE RESTRICT |
| NoAction | database default |
| SetNull | ON DELETE SET NULL |
| ClientSetNull | database default |
| ClientCascade | database default |
| ClientNoAction | database default |
Влияние на поведение метода SaveChanges
В следующих разделах приводятся таблицы, в которых описывается, что происходит с зависимыми или дочерними сущностями при удалении основной или родительской сущности, а также при разрыве ее связи с зависимой или дочерней сущностью. Каждая таблица описывает одну из следующих ситуаций.
Обязательная связь с загруженными зависимыми или дочерними сущностями
| Значение перечисления DeleteBehavior | Поведение при удалении основной или родительской сущности | Поведение при разрыве связи с основной или родительской сущностью |
|---|---|---|
| Cascade | EF Core удаляет зависимые сущности | EF Core удаляет зависимые сущности |
| Ограничение | InvalidOperationException | InvalidOperationException |
| NoAction | InvalidOperationException | InvalidOperationException |
| SetNull | При создании базы данных возникает исключение SqlException | При создании базы данных возникает исключение SqlException |
| ClientSetNull | InvalidOperationException | InvalidOperationException |
| ClientCascade | EF Core удаляет зависимые сущности | EF Core удаляет зависимые сущности |
| ClientNoAction | DbUpdateException | InvalidOperationException |
Обязательная связь, для которой не загружены зависимые или дочерние сущности
| Значение перечисления DeleteBehavior | Поведение при удалении основной или родительской сущности | Поведение при разрыве связи с основной или родительской сущностью |
|---|---|---|
| Cascade | База данных удаляет зависимые сущности | Н/Д |
| Ограничение | DbUpdateException | Н/Д |
| NoAction | DbUpdateException | Н/Д |
| SetNull | При создании базы данных возникает исключение SqlException | Н/Д |
| ClientSetNull | DbUpdateException | Н/Д |
| ClientCascade | DbUpdateException | Н/Д |
| ClientNoAction | DbUpdateException | Н/Д |
Необязательная связь с загруженными зависимыми или дочерними сущностями
| Значение перечисления DeleteBehavior | Поведение при удалении основной или родительской сущности | Поведение при разрыве связи с основной или родительской сущностью |
|---|---|---|
| Cascade | EF Core удаляет зависимые сущности | EF Core удаляет зависимые сущности |
| Ограничение | EF Core присваивает значения NULL внешним ключам зависимых сущностей | EF Core присваивает значения NULL внешним ключам зависимых сущностей |
| NoAction | EF Core присваивает значения NULL внешним ключам зависимых сущностей | EF Core присваивает значения NULL внешним ключам зависимых сущностей |
| SetNull | EF Core присваивает значения NULL внешним ключам зависимых сущностей | EF Core присваивает значения NULL внешним ключам зависимых сущностей |
| ClientSetNull | EF Core присваивает значения NULL внешним ключам зависимых сущностей | EF Core присваивает значения NULL внешним ключам зависимых сущностей |
| ClientCascade | EF Core удаляет зависимые сущности | EF Core удаляет зависимые сущности |
| ClientNoAction | DbUpdateException | EF Core присваивает значения NULL внешним ключам зависимых сущностей |
Необязательная связь, для которой не загружены зависимые или дочерние сущности
| Значение перечисления DeleteBehavior | Поведение при удалении основной или родительской сущности | Поведение при разрыве связи с основной или родительской сущностью |
|---|---|---|
| Cascade | База данных удаляет зависимые сущности | Н/Д |
| Ограничение | DbUpdateException | Н/Д |
| NoAction | DbUpdateException | Н/Д |
| SetNull | База данных присваивает значения NULL внешним ключам зависимых сущностей | Н/Д |
| ClientSetNull | DbUpdateException | Н/Д |
| ClientCascade | DbUpdateException | Н/Д |
| ClientNoAction | DbUpdateException | Н/Д |




