Pull to refresh

Comments 8

При сохранении записи на курс со ссылкой на студента, которого больше нет в БД, мы получим ошибку ограничения внешнего ключа. По хорошему на сервере нужно было заранее об этом подумать и сделать каскадное удаление данных, чтобы вообще не допускать этой проблемы.

Вот как-то совсем не понял, как вообще может возникнуть эта ошибка... если студента в БД нет, откуда взялась запись, которую вы пытаетесь сохранить? как она вообще могла появиться?

Со стороны интерфейса, как я понимаю, оно выглядит так: ткнули в студента, ткнули в курс, нажали кнопку "записать выбранного студента на выбранный курс". И описанная проблема может возникнуть только в случае, если с момента загрузки списков, пока расставляются галки, кто-то другой этого выбранного студента удалил.

Ну так это не проблема внешнего ключа, это проблема непродуманного алгоритма многопользовательской работы. Достаточно изменить запрос с простого, но ничего не проверяющего

INSERT INTO Registrations (StudentID, CourseID) VALUES (@StudentID, @CourseID);

на надёжное

INSERT INTO Registrations (StudentID, CourseID)
SELECT Students.StudentID, Cources.CourseID
FROM Students
CROSS JOIN Cources
WHERE Students.StudentID = @StudentID
  AND Cources.CourseID = @CourseID;

И, собственно, всё. Ошибка умерла, даже не родившись. А, посмотрев на affected rows, можно сказать, успешно выполнена запись, или кто-то успел студента удалить.

Да, также вообще не понял, каким тут боком каскадное удаление. Так, для красного словца? Мы же вроде вставляем новую запись...

Мы сталкиваемся с этой проблемой в мобильных приложениях, когда синхронизируем данные c сервера и сохраняем их в базу данных SQLite.

Пример вымышленный, однако практика показывает, что такое может случиться. Я представляю себе ситуацию так: в административной панели по какой-то причине был удален студент, однако запись о его участии в курсе осталась (почему это произошло — вопрос к бэкенду). Каскадное удаление решило бы проблему появления "висячих" записей, так как при удалении родительской записи "студент" удалилась бы и "запись на курс" с этим студентом. (Возможно есть и другие ситуации, которые приводят к появлению таких записей, в том числе и из-за неправильно логики на бэкенде или мобилке).

При сохранении в мобильном приложении можно было бы добавить проверку на наличие студента в записи на курс, которую мы пытаемся сохранить, но тогда мы не выявим корень проблемы и могут возникнуть другие ошибки связанные уже с логикой в мобильном приложении (часто лучше быстро получить ошибку и быстро ее поправить, чем вообще не подозревать о ее наличии). Плюс из-за дополнительных проверок на наличие родительских записей, скорость синхронизации в МП просядет, что вызовет гнев пользователей.

Из-за появления ошибок такого рода, мы решили улучшить ее описание при возникновении в мобильном приложениях, чтобы можно было быстро понять в чем проблема и пофиксить ее (все таки мы люди и нам свойственно ошибаться).

Ааа... так это просто пример неудачный! Вот если бы базово рассматривалась именно заливка данных извне (грубо - контрагент прислал список записанных им на выезде потенциальных обучающихся, к примеру), где по определению может быть не всё ладно, тогда было бы сразу понятно, почему возможные проблемы проще именно допустить, а их причину проанализировать - потому что на каждую возможную граблю никаких запросов не хватит.

А ещё лучше, если бы рассматривалась именно проблема "кривого" запроса - да хоть с опечаткой. Это я к тому, что возможно в наборе добавляемых данных наличие просто кучи приводящих к ошибке ляпов, но отловится только один из них, а остальные - лишь после исправления, на следующем запуске. Почему и ратую за то, чтобы хотя бы самые очевидные из таких ляпов не допускались самОй логикой кода.

На практике, каскадное удаление на бэкенде почти нигде не используется, потому что может приводить к страшным багам в ORM, при неосторожном использовании или ошибках в самом ORM, вплоть до удаления всей базы в ходе путешествия по каскадам. Гораздо эффективней иметь поле "запись удалена" и устанавливать туда правильный флаг. За одним, такой подход позволяет реализовать отмену операций удаления или просмотр истории всех операций и прочие кейсы путешествий в прошлое.

На практике, каскадное удаление на бэкенде почти нигде не используется, потому что может приводить к страшным багам в ORM, при неосторожном использовании или ошибках в самом ORM, вплоть до удаления всей базы в ходе путешествия по каскадам.

Ну так это либо бага в ORM (что вряд ли), либо кривая логика бэка, либо кривая реализация этой логики. В любом случае - это проблема вовсе не сервера БД.

Гораздо эффективней иметь поле "запись удалена" и устанавливать туда правильный флаг.

Мягкое удаление - это, конечно, вариант. Но он утяжеляет БД, поскольку в ней будет мотаться куча записей, которые теперь надо в абсолютно каждом запросе фильтровать. Уж лучше думать об архивной таблице и сливать туда всё удаляемое триггером BEFORE / INSTEAD OF DELETE. Решение в общем тоже не сахарное, база по-прежнему будет пухнуть. Две БД и перемещение из одной в другую - это уже операция через клиента, и тут проблем вагон и маленькая тележка. И всё это - руками, ибо версионку и temporal tables я как-то в SQLite не припоминаю.

или просмотр истории всех операций и прочие кейсы путешествий в прошлое.

Не всех. Только удаление - апдейты таким способом не поймать. И глубина истории будет всего в одно действие. Или придётся делать полноценную систему журналирования всех действий с данными.

Борьба с ветряными мельницами. Такую ошибку с бд надо отслеживать на сервере или даже заменить удаление записи с первичным ключем просто на перенос в неактивные или архив, если возможно.

Так в статье и говорится о том, что эту ошибку нужно исправлять на сервере, вот только если все таки допустили ошибку и в приложение пришли не консистентные данные, то благодаря методам, которые описанны в статье, можно будет быстро вычислить ошибку и поправить. Иначе просто в связи плохого описания ошибки, пришлось бы дергать мобильных разработчиков, чтобы они нашли проблему, а их время не дешево стоит. К тому же эту ошибку можно допустить и в мобильном приложении, и эти методы также позволят быстро разобраться в проблеме.

Sign up to leave a comment.