Антипаттерны работы с базами данных

Привет, Хабр! Представляю вашему вниманию перевод моей статьи «Database: Anti-Patterns».

Если вы храните данные, это критически важная часть вашего приложения. Можно легко и быстро задеплоить исправление бага на новом сайте знакомств, чтобы фермер Джо из северного Техаса наконец-то смог прочитать последнее сообщение от любовницы по переписке и узнать, что ей нравятся лысые мужчины. Но упаси вас бог потерять или испортить пользовательские данные.

image
Кремниевая долина, 2 сезон, 8 серия.

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

Вот лишь те, что сразу приходят в голову.

Отсутствие бэкапов


image

«Делайте бэкапы» — одно из таких правил (вроде «не работай под рутом» или «пристегивайте ремни»), с которыми многие из нас согласны, но не следуют им, надеясь, что плохие вещи случаются с другими, а не с нами.

Кстати, если вы не тестируете восстановление из бэкапов, можете считать, что бэкапов у вас нет вообще. Учитесь на чужих ошибках:
Другими словами, из пяти задеплоенных техник резервного копирования / репликации ни одна не работает надежно или не настроена. В конце концов, мы восстановили данные из бэкапа, сделанного 6 часов назад.
Мы потеряли данные базы за 6 часов (issues, merge requests, пользователи, комментарии, сниппеты и др.) с GitLab.com.

NoSQL


Бывает, что у ваших пользователей слишком много контента для взрослых и они слишком часто его смотрят объем данных слишком велик или нагрузка слишком высока, чтобы реляционная БД могла справиться. Это тот случай, когда в игру вступают NoSQL-технологии. Софтверные гиганты вроде Google знакомы с этим не понаслышке.

Но вы не Google. Несколько сотен гигабайт — это не «big data», а 1000 комментариев в день — не «high load». Скорее всего, для ваших данных вполне достаточно PostgreSQL. Смотрите: он даже поддерживает JSON и умеет его индексировать.

Бросьте, вы серьезно хотите пожертвовать надежной структурой ради фич, которые вам не нужны и — посмотрим правде в глаза — никогда не будут нужны? Вы не станете новым Гуглом — у вас просто будет бардак в базе.

Слишком свободная схема


Это более актуально для NoSQL, но пользователи реляционных СУБД зачастую забывают или ленятся создавать все нужные ограничения. Из-за ошибки в коде приложения NULL может быть сохранен там, где ожидается осмысленное значение, или может быть создана ссылка на отсутствующую запись. Впоследствии вы это замечаете и исправляете код, но понятия не имеете, как исправить данные.

Естественные первичные ключи


Представим, что мы хотим хранить пользователей, у каждого из которых должен быть уникальный e-mail. Самое очевидное решение — создать таблицу user с колонкой email, которая будет еще и первичным ключом.

К сожалению, естественный ключ может стать неприемлемым в качестве первичного при изменении требований (а они меняются постоянно). Сегодня PRIMARY KEY(email) работает, а завтра мы решаем добавить регистрацию через Facebook и сделать e-mail необязательным. Что лучше: генерировать уникальные адреса и добавить флаг, обозначающий фиктивный e-mail, или изменить первичный ключ, все внешние ключи, которые ссылаются на user, и т.д., и т.п.? Нам не пришлось бы выбирать меньшее из зол, если бы мы просто использовали суррогатный первичный ключ.

Логика в хранимках


Мне это не нравится по двум причинам:

  1. Код приложения обычно намного проще обновить, чем схему БД.
  2. Все эти PL SQL-ли напоминают мне Паскаль, и они такие же уродливые.

Миграционные скрипты, специфичные для конкретных сред


Я знаю, что иногда выбора нет, но в целом лучше стараться, чтобы все среды (dev, test, prod и т.д.) были как можно более похожими. Чем больше разница между средами, тем больше шансов допустить ошибку и обнаружить это только на проде.

Обычно даже DML-скрипты могут быть универсальными. Разные схемы же чаще всего — чистое зло.

Поэтому, когда я вижу в Liquibase-скриптах метки (labels), специфичные для конкретных сред, мне хочется убивать.

Толерантные миграционные скрипты


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

Неатомарные обновления


Допустим, вы запустили changeset на продакшн-базе, и миграция не прошла успешно. Вы что-нибудь исправляете и хотите попробовать снова. Получится ли? Что если одни операции changeset-а закоммитились, а другие нет?

Вы можете заметить, что на самом деле это история о том, что changeset-ы должны быть идемпотентными, и будете правы.

К сожалению, многие разработчики, думая об идемпотентности, используют IF NOT EXISTS или типа того. В предыдущем разделе я объяснил, почему это зло.

Вместо этого делайте changeset-ы атомарными. Тогда в случае ошибки совершенные изменения будут откачены и у вас не будет проблем с последующим применением этого changeset-а.

Но будьте внимательны, рассчитывая на транзакции. Например, поддержка DDL-выражений в транзакциях MySQL темна и полна ужасов, так что я всегда создаю отдельный changeset для каждого DDL-выражения, когда пишу Liquibase-скрипты для MySQL.

А какие антипаттерны видели вы?

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 476

    +4

    Хорошая статья. На счёт NoSQL — на мой взгляд он просто для других задач. При чем если SQL базы похожи то NoSQL более узконаправленные и не значит что они не нужны обычным пользователям. Та же Монго хорошо подходит для хранения JSON документов, ElasticSearch для поиска и так далее.

      +45
      Во-первых, всем этим советам сто лет в обед. А во-вторых,

      >Все эти PL SQL-ли напоминают мне Паскаль, и они такие же уродливые.
      Мне тоже логика в хранимках не очень нравится, но вот это — просто зашибись какой аргумент.

      Ну то есть, если вы присмотритесь к уровню аргументации, то вы увидите, что все пункты либо очевидны и не новы, либо аргументированы примерно так же хреново, как этот вот про NoSQL.
        +3
        Я знаю кучу случаев, когда логика на уровне БД просто необходима.

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

        Вы можете либо перенести всю расчетную логику в сам запрос (селект на 3 страницы), что, по сути, та же логика БД, записанная где-то в приложении, написать задачу по пересчету процентов каждый день (тоже на БД) или воспользоваться computed column.

        В любом случае, просто в приложении не будет.
        Ну и аргумент, что напоминает паскаль просто убило. Каждой задаче — свои инструменты.
          +1
          >Я знаю кучу случаев, когда логика на уровне БД просто необходима.
          Так я и не говорил, что их не бывает.
          • UFO just landed and posted this here
              +1
              Что именно у вас там будет асинхронно читаться? Вы представляете как Join работает?
              Предположим даже, что необходимые джоины мы сделали на сервере, и теперь нам надо просто загрузить записи на клиент по списку Id и отфильтровать по вычисляемым полям (что естественно не так на самом деле, так как нужны данные из сджоинных таблиц тоже).
              Даже если вы асинхронно считываете записи, то вам все равно нужно перебрать все записи по очереди, чтобы найти удовлетворяющие поиску. А потом нужно будет еще это отсортировать по полям и выдать пагинацию.

              Все это вы будете делать в памяти сервера приложений, потому что
              Хотя бы потому что отладчиком воспользоваться вы не сможете для анализа работы скрипта


              На самом деле, никто не отменял логирование этапов работы хранимки в таблицу, например. Также никто не отменял таблицу с референсными данными и результатами, по которой проверяются все бизнес-значимые хранимки при изменениях.
              • UFO just landed and posted this here
                  +2
                  Представляю, и не понимаю, как join мешает асинхронной выборке. Join будет в любом случае, где бы вы не обрабатывали данные, на сервере App или сервере DB. Сам расчет вы можете вынести на сервер приложений. А асинхронный ридер может вам отдавать записи для расчета по мере выполнения запроса, именно такая схема позволяет обсчитывать большие объемы без большой нагрузки на сервер приложений.


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

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


                  Опять мимо. Сортировка требует, что удивительно, сортировки. Вы не можете выдать записи в порядке, не упорядочив их все. Поэтому если вы не используете БД, то опять же, вы закачаете все данные и будете их сортировать.

                  Офигенный костыль, который в моем случае, например, мне надо еще сделать самому. Я такие вещи делал когда сайт на asp в блокноте делал. Сейчас тоже могу, но зачем?
                  Согласитесь, что отладчик в IDE несравнимо удобнее. Например, позволяет удобно и быстро перемещаться между вызовами вверх и вниз, останавливать выполнение.


                  База данных — это специализированный инструмент, позволяющий решать отдельные задачи. И инструменты работы с ними другие. Дебагинг, в том или ином виде, есть практически везде. То, что в IDE удобнее нивелируется тем, что работать с миллиардами записей вы можете только с СУБД.
                  • UFO just landed and posted this here
                      +2
                      Да, похоже мы не понимаем друг друга.

                      Я привел конкретный пример. Сотни миллионов записей. Процент доходности и другие параметры, пересчитываемые каждый день. И выборка с сортировкой по этим параметрам.

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

                      Могу. И даже вчера делал и работает, что удивительно. Данные запроса c Order by нормально читаются асинхронно.


                      Запрос Order By на сервере БД уже упорядочил записи. Вы уже тяните отсортированные данные. Асинхронно или синхронно (есть интересно в 2020 году синхронные ОРМ?) это дело десятое.
                      +1
                      Судя по всему, вы слабо понимаете, как работает Join. Если код выполняется на сервере приложений, то по сути, это нестед луп. У вас для каждой строчки большей таблицы происходит поиск по всем! строчкам меньшей таблицы.
                      Из этого простого свойства мы видим, что меньшая таблица, в любом случае, будет закачана в память целиком.

                      Не обязательно. Делаем два запроса к источнику данных: после первого выбираем нужные id для второго, делаем запрос на получение строк для этих id, складываем в хэшмапу и мержим два в одном цикле. Поиск по хэшмапе — это не цикл в общем смысле слова.

                        +1
                        Поиск по хэшмапе — это не цикл в общем смысле слова.

                        То, что вы описали — это временное копирование базы данных в память приложения. Причём для общего случая повторять придётся для каждого запроса, и повторять весь фарш, включая пересылку кусков базы по сети, и ещё неизвестно, какой вариант получится производительнее. В базе данных тоже ведь бывает hash map при выполнении запроса.
                        Я, конечно, допускаю, что такой подход работает хорошо для объединений с enum, которые можно просто держать в памяти любого appserver'a, потому что они всё равно не меняются. Или если таблицы лежат вообще в разных системах, но критерий объединения исчислимый. А вот для случая единственной базы это имеет смысл только если у вас отчего-то жёсткое ограничение на мощность dbserver'a, а для appserver'ов ограничения нет, и их можно заставлять молотить тупейшие вещи с гигантскими потерями в эффективности на rountrip'ы между серверами.

                          +2

                          Нет, не копируем всю базу в память. База, пускай, по договорам нескольких сотен тысяч клиентов, у многих по несколько догоовров. Смотрим реестр договоров за день — пускай тысяча записей из таблицы договоров с текущей датой, выбираем их в приложение, выбираем все customer_id — те же несколько тысяч, может немного меньше. Выбираем данные из таблицы клиентов и помещаем их в хэшмапу, потом джойним, да, плюс-минус, как база джойнит. Копируем из базы в приложение только пару тысяч записей.


                          Про ограничения я ничего не говорил, вы меня с кем-то путаете.

                            +1
                            А потом, внезапно, окажется не реестр договоров, а транзакции, и их станет не десять тысяч, а десять миллионов, они перестанут помещаться в памяти, GC начнет страдать и вся производительность будет деградировать адским образом. Причем по всему серверу приложений.

                            Зачем изобретать велосипед, если на стороне СУБД профессионалы уже решили все эти вопросы много лет назад?
                              +1

                              Я всего лишь привёл пример как можно избежать джойнов на стороне БД. И то в данном контексте теоретический, поскольк против джойнов ничего не имею (в отличии от вьюх, хранимок, триггеров и прочей логики хранимой в БД т. д.), на практике такой подход применяю в рамках сервисной архитектуры, когда таблицы оказываются в разных базах. Если же апп сервера не хватает, то отдельная БД для джойнов и прочих read моделей, который управляется событиями с сервисов типа "данные кастомера изменились на вот эти", "вот такой новый договор у этого кастомера", а сервис агрегации записывает это в одну плоскую таблицу.

                                +1

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

                                  +1

                                  Они хранятся на сервере БД. Одних таблиц вполне достаточно для желания иметь поменьше дела непосредственно с БД.


                                  Не, на самом деле с системой, которая хранит в VCS декларативное описание схемы базы, включая даже хранимки и триггеры и генерирует "патчи" по диффу, работать можно, наверное, обычному прикладному программисту. Но я такой не то, что не встречал, а только в этом топике в первый раз услышал, что это возможно. Я просил часов 300 на MVP такой системы для внутреннего пользования — не дали.

                                    0
                                    хранит в VCS декларативное описание схемы базы

                                    У нас есть подобное описание в XML, оные файлы хранятся в VCS, и при накатывании патча есть специальный скрипт с возможностю сделать diff между тем, что в XML и указанной базой данных, оно сгенерит патчи с изменениями. Правда, работает только для таблиц и некоторых типов constraint'ов, и патчи накатываются как минимум с контролем мясного человека, но, я считаю, начало положено. Наши вьюшки и процедуры лежат там же, рядом, по ним просто делается DROP..CREATE, потому что они данных не содержат. На самом деле сложнее всего делать миграцию данных, а не схемы. Вот эта миграция пишется всегда руками, но и эти патчи тоже лежат в VCS.


                                    Я просил часов 300 на MVP такой системы для внутреннего пользования — не дали

                                    Ну да, в том числе ещё и потому, что вокруг очень много людей с вот этим непонятным мне убеждением, что база данных — это не важная часть приложения, и программистам мараться с поддержкой базы негоже. С другой стороны, 300 — это, на мой вкус, многовато. Возможно, стоило начать с небольшой автоматизации, какой-нибудь части, чтобы народ распробовал и было проще выбить время уже на улучшение, а не разработку с нуля.

                                      0

                                      Ну вот для таблиц оно в той или иной форме много где есть, например в некоторых ORM.

                                        +1
                                        много где есть, например в некоторых ORM.

                                        И это хорошо. Я не антагонизирую ORM во всех вопросах — только в некоторых, и простейшая миграция не входит в их число. Та часть ответа вообще больше про хранение описания схемы в VCS (а точнее про то, что у нас схема и много чего ещё создаётся как следствие файлов, а файлы — в VCS).


                                        sergeaunt


                                        а) не полны, б) надо проверять, в) я всё равно напишу лучше.

                                        Если можно автоматизировать часть работы — почему этого не сделать? Да, инструменты автомиграции неполны. Но вы, к примеру, автокомплитом кода пользуетесь? Он ведь тоже не полный, да и код пишет хуже вас.


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

                                          0
                                          Но вы, к примеру, автокомплитом кода пользуетесь? Он ведь тоже не полный, да и код пишет хуже вас.

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

                                        У меня обратное убеждение: БД — это самая важная часть приложения, и нельзя доверять генерялкам миграций. По сути они бесполезны, т.к. а) не полны, б) надо проверять, в) я всё равно напишу лучше.
                    0
                    Хотя бы потому что отладчиком воспользоваться вы не сможете для анализа работы скрипта
                    Это в какой СУБД нет дебаггера?
                    • UFO just landed and posted this here
                        +1
                        Дебаггер то есть, а вот прав для его использования не дают. Что в общем-то логично, учитывая количество разработчиков, одновременно работающих с базой.


                        Вы дебажите «боевую» базу???
                        Программируете «на живую»???
                        • UFO just landed and posted this here
                            +1
                            Разве я так написал? )
                            На тест достаточно прав для запуска дебагера тоже не дают.

                            Если не дают доступа на тестовой — нужно поговорить с руководством.
                            Это в нём косяк, а не в СУБД.
                            • UFO just landed and posted this here
              +1
              Бессмысленно призывать к разумности неразумные среды. Бессмысленно призывать к возможности планирования там, где планирование не предусмотрено изначально по определению и считается врагом, а главной задачей БД и админа БД руководителями видится как уборка и сортировка мусора выплывающего из голов разработчиков. «А иначе зачем все эти бездельники (админы БД) нужны?» Сначала надо отойти от главного зла. Один из руководителей разработчиков как-то заявил, что программисты пишут код, а эффективно его выполнять это задачи самой БД и администраторов БД. И точка. А отсутствие всех этих NOT NULLов, IF NOT EXISTSов, элементарного правильного выбора типов колонок и прочее, прочее, прочее это уже прямое и неизбежное следствие.
                +3
                Т.е. вместо того, чтобы выкатить один патч с полусотней DDL, мне надо разбивать его на полсотни атомарных патчей?
                А чтобы не бояться IF NOT EXISTS помогают тесты.
                И мне несравнимо проще протестировать результат установки одного моего патча, нежели протаскивать через Devops-трубу и все официальные процедуры (без которых не поставить патч на ПРОМ-среду) полсотни патчей.

                Имхо, антипаттерны, приведенные в статье, являются таковыми только в некотором окружении, но далеко не универсальны.
                  +4
                  По проблемам управления изменениями можно написать много антипаттернов. Самый частый, что я видел- нежелание вести весь код и ddl в репозитории.
                    +1
                    выкатить один патч с полусотней DDL

                    Проблема, возможно, кроется именно здесь. Если патчи на полсотни DDL выкатываются каждый релиз — это очень странно. Что заставляет вас менять структуру БД так часто?


                    Идея же была в том, что атомарными миграциями проще управлять. Мы живём в неидеальном мире, и тот факт, что на вашей машине миграция прошла успешно, не сделает автоматически успешным применение миграции на проде.


                    Вы никогда не оказывались в щекотливой ситуации, когда большая миграция зафейлилась посередине непонятно где? Мне в своё время повезло — я по логам выяснил, на каком этапе упала миграция, удалил из файла миграции уже применённое, поправил ошибку — и миграция донакатилась. Посыл в том, что лопатить логи тоже не дело. Если бы миграция была атомарной, я бы её накатил в несколько этапов, а при ошибках даже не задумывался бы, что уже сделано, что нужно откатить, как сплитнуть миграцию.

                      0
                      когда большая миграция зафейлилась посередине непонятно где

                      Транзакции? Специальные инструменты для миграций с откатом изменений? Тестовый прогон на копии прода с бекапом?
                        +2
                        Работает, но не всегда. А ещё бывает, что копия прода получается не совсем копией, и тогда тестовый прогон маскирует будущую огромную проблему при деплое на реальный продакшен. Так не бывает, воскликнете вы, но нет, бывает. Особенно если база прода — сама по себе старая, ей уже лет 20, и в ней накопились останки мамонтов и прочие донные отложения.
                          0
                          Логика есть определенная. Просто не делайте сильно большие изменения за раз. Сколько это много приходит только с опытом.
                            +1

                            Не все базы поодерживают транзакции для DDL

                            0
                            Если патчи на полсотни DDL выкатываются каждый релиз — это очень странно. Что заставляет вас менять структуру БД так часто?

                            Изменяющиеся требования, вестимо. Особенно, если работать по DDD: решил бизнес переименовать, например, одно поле в UI — его надо переименовывать от UI до базы, чтобы был один термин, чтобы не было каких-то таблиц (хорошо если не в уме) перевода UI->App->DB и может ещё какие-то слои.

                              0
                              DDD — это больше о доменной логике, а не об инфраструктуре и презентации. Маппинг не противоречит DDD.
                                0

                                Может и не противоречит, но сильно снижает пользу от него. Единый язык на всех уровнях очень упрощает коммуникации. Бизнес часто оперирует терминами UI, говорит "вот в поле "контакт организации отчёта показывается не то имя котрое я ввёл в форме создания контакта", продираться через маппинги (хорошо, если они явные и декларативные на границах слоёв, а не где-то по кусочкам в череде условий собираются) может быть очень долго. А так заглянул в базу в organization_contact.name и посмотрел сразу, оно неправильно создаётся или неправильно выводится. А если контакты хранятся в таблице users а имя в поле fullName (поле name тоже в наличии), то локализовать баг может занять очень много времени, если всех нюансов в оперативной памяти не держишь.

                                  0
                                  Я не спорю, что одна терминология в БД и UI всегда удобнее, чем разная. Вне зависимости от DDD-мышления. Так по умолчанию делается в любом CRUD и где угодно при начальной разработке. Просто это сильно небесплатно при развитии проекта, поэтому от этого допустимо отказаться и никак не повлиять на доменную ориентированность кода. БД вообще может быть спроектирована 20 лет назад и иметь инопланетную структуру, только маппинг усложнится.
                                    0

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

                                      0
                                      Я, честно говоря, не припомню упоминания про инфраструктуру в DDD статьях и книгах. Мне кажется, вы расширили терминологию. Не то что я против, но это вызывает недопонимание.
                                        0

                                        Инфраструктура точно упоминается. Реализация интерфейсов репозитория из доменной области классически располагается в инфраструктурном слое, как и маппинг сущностей домена на таблицы БД. И в книгах и статьях исходники часто явно бьются на App, Domain и Infra(structure) каталоги/неймспейсы.


                                        Прямо вроде не постулируется, чтобы, например, таблицы и их колонки назывались одинаково с классами и их свойствами, то же и про UI, но это же вытекает из целей создания единого языка — не должно быть переводчиков с языка бизнеса на язык разработчиков, даже в теории не должно в разговоре эксперта и разработчика проскользнуть что-то вроде: — у нас в таблице пользователей нельзя создать две записи с одинаковым email


                                        • а что это за таблица?
                                        • ну это то, что вы называете контактами организации

                                        Разве нет?

                                          0
                                          не должно быть переводчиков с языка бизнеса на язык разработчиков
                                          Само собой, для этого служит модель бизнеса. Но модель бизнеса — это доменный код, инфраструктура не отражает модель, а является обслуживающим ядро слоем. Сама БД из-за собственных ограничений вообще неспособна отражать бизнес-модель, один термин может быть раскидан по 10 таблицам и наоборот, или вообще лежать в каком-нибудь Redis queue. В инфраструктуре существует чисто технический код — запросы, синхронизации и всё такое. Даже получение и сохранение объекта — это скорее техническая, а не бизнес задача, выполняемая на границе слоя Application.
                                          Разве нет?
                                          Нет, бизнес этого не касается, как и не касается Application слоя, ему это безразлично и вообще не разбирается. Он разбирается только в своей логике. Когда разработчик говорит с бизнесом, он говорит о бизнес-логике. По идее бизнес не говорит о поле в таблице, это уже особенности конкретного бизнес-аналитика или кто там вместо него, который интересуется технической частью.
                                          UI немного неизбежно отображает доменную область, в каком-то роде и сам — часть бизнес-процессов.
                                          Это то, что мне было понятно из книг из статей о DDD, которые упирали именно на моделирование бизнеса. Поля БД удобно маппить 1 к 1 к домену, но некритично. DDD — дорогой способ разработки, но не из-за БД, а из-за крайней неуниверсальности.
                                            0
                                            В инфраструктуре существует чисто технический код — запросы, синхронизации и всё такое. Даже получение и сохранение объекта — это скорее техническая, а не бизнес задача, выполняемая на границе слоя Application.

                                            Но принимает эта инфраструктура на вход и(или) отдаёт на выход сущности и VO доменной области. В крайнем случае DTO из неё же, но даже в этом случае чтобы обеспечить независимость доменной модели от инфраструктуры они отражают структуру сущностей и VO.


                                            Даже если конкретная сущность будет размазана по нескольким таблицам, базам и другим стораджам, согласно целям и принципам DDD нейминг их должен соответствовать домену. Единый язык домена, какого-то контекста должен проникать во все слои системы настолько насколько это возможно. Своего рода исключение, пожалуй, только слой адаптеров между разными контекстами, где какой-нибудь user.name одного контекста маппится на contact.fullName другого.


                                            DDD — дорогой способ разработки, но не из-за БД, а из-за крайней неуниверсальности.

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


                                            В трехзвенных и подобных архитектурах перевод "термин UI"<->"термин модели UI приложения"<->"термин API апп-сервера"<->"термин доменной модели"<->"термин инфраструктуры"<->"термин БД" может отнимать достаточно много ресурсов, а, главное, приводить к дополнительным ошибкам "перевода".


                                            А в чём неуниверсальность?

                                              0
                                              требует (ну или очень рекомендует) постоянных сквозных изменений неймингов и структур данных при любом уточнении доменной модели
                                              По сути только РСУБД и позволяют более менее удобно это делать, хотя при росте данных, нагрузке и распределенности уже немножко не позволяют. Поля в Elasticsearch например вообще нельзя переименовать и удалять без пересоздание индекса. Или представим БД высоконагруженного приложения в виде Redis, где ключи вида «fullName.{name}» содержат в себе сериализованную структуру вида {dob: string, id: int}. И это может еще параллельно в очереди MQ «fullNamesQueue» лежать. Переименовывать тут что-то наживую или пересоздавать по сути ресурсы — тот еще адок. Есть еще более экзотические примеры, например AWS S3. При этом игнорировать DDD при сложном домене для приложения не на одной лёгкой РСУБД сомнительно. Лучше оставить в стороне мысль о жесткой связи инфраструктуры и домена.
                                              польза от него снижается, субъективно, больше чем повышается от выделения доменной модели в независимое ядро
                                              Зависит от инструментов и инфраструктуры. Хотя ORM позволяют бесплатно переименовывать поля и сущности. А вот изменения побольше при использовании ORM болезненны.
                                              А в чём неуниверсальность?
                                              Я имею ввиду, что при обычном подходе бизнес-логика «вмазывается» в существующие фреймворки и инструменты и поддерживается в соответствии с их требованиями, так универсальнее и дешевле в разработке.
                                                0
                                                По сути только РСУБД и позволяют более менее удобно это делать, хотя при росте данных, нагрузке и распределенности уже немножко не позволяют.

                                                Я это понимаю. Но техники типа zero time или green/blue deployment довольно универсальны.


                                                Лучше оставить в стороне мысль о жесткой связи инфраструктуры и домена.

                                                Идеал недостижим, но к нему следует стремиться :) Если ресурсы позволяют :(


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

                                                Как по мне, то как раз это неуниверсально, а довольно жёсткий вендор-лок, на практике особенно больно от лока на конкретные реализации ActiveRecord ORM. DDD же по сути продвигает framework agnostic подход для, как минимум, доменной области, да и для уровня приложения можно применить, вынеся всю, завязанное на фреймворк, в инфраструктурный уровень, реализующий точки входа и интерфейсы для application и domain уровней. Не всегда просто, но гораздо универсальней.

                          +1
                          Кстати, разные схемы не такое уж и зло, если нужно несколько стендов развернуть на одном инстансе СУБД. Это, конечно, добавляет хлопот при подготовке кода к установке, но не сказать, что хоть сколько-нибудь критично.
                            +1
                            Под «разными схемами» вы подразумеваете разные имена схем? Это, действительно, не страшно. А вот когда, например, на тесте есть некая таблица, а на проде — нет, рождается зло.
                            если нужно несколько стендов развернуть на одном инстансе СУБД

                            То в случае с тем же PostgreSQL проще и надежнее сделать отдельные базы, не?
                            +21

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

                              +2
                              В хранимках ещё бизнес-логика часто делается. И отлаживается это спокойно и отдельно от кода приложения.
                                +7
                                А потом клиент не знает что и делать, когда 10-15 лет спустя решает переехать на другую СУБД, а там сотни недокументированных SPs на PL/SQL или T-SQL. Дошло до того, что некоторые инструменты для планирования миграций даже умеют примерно прикидывать необходимые ресурсы для переписывания всего этого зоопарка (типа у вас найдено 100500 SPs — наймите 100 разработчиков и через 50 лет все закончите)
                                  +5
                                  Через 10-15 лет клиент может захотеть вообще всё переписать на новой языке. Так что это надуманная проблема. Но хранимки лично для меня дают возможность чётко разграничить работу в более-менее сложном проекте. Ответственные операции делаются на сервере и там же всё отлаживается. Клиентской части, как правило, остаётся ввод-вывод данных и их отображение.
                                  Кроме того, помимо хранимок есть такие крайне важные вещи как триггеры. Без них в серьёзной базе никуда, если требуется жёсткий контроль ссылочной целостности. А объём кода там тоже может быть приличным.
                                    +12
                                    База антипаторнов в одном комментарии.
                                    Базы данных живут и более 15 лет, нашей, например, около 20 лет, а бизнес логика может и поменятся за это время много раз. Они, прежде всего, предназначены для хранения и поиска данных, а любая бизнес логика выносится выше на уровень бинес логики и выполняется сервером приложений. И делается это именно для того, чтобы можно было нормально разграничиать логику и данные, отдельно поддерживать и код и данные и не отлаживать на сервере баз данных, а через тесты. Хранимки же, очень плохо отлаживаются и зависят от окружения.
                                    Триггеры не предназначены для организации ссылочной целостности. Для этого есть внешние ключи и констрэйнты. Если вы заложите в триггеры много логики, то получите сильный просад по скорости работы и на больших объемах, миллионы записей, это будет катастрофой.
                                      +2
                                      У вас трёхзвенка, но не у всех так. Поэтому и подходы разные.
                                      Да и вообще глупо как-то пропагандировать только один подход и называть его верным.
                                        +1
                                        Подходы придумали не так просто, а чтобы избежать проблем при поддержке. В любой схеме база не должна выступать на уровне логики. Она может выступать хранилищем огики, но не должна занимать ее исполнением.
                                          +4
                                          Только уточняйте, что это исключительно «ваши» подходы. И «база не должна выступать на уровне логики» это также «ваш» подход. Но существует громадное количество задач, что этот подход плохо применим. Поэтому навязывать его везде не следует.
                                            –5
                                            Почему же это мои подходы? Опыт нескольких поколений разработчиков в построении систем автоматизации разве ничего не значит? Я просто использую опыт этих поколений, а не только использую свой. Например, за 20 лет разработки в производственном, торговом, финансовом и прочем бизнесе я использовал хранимую процедуру только один раз и то из-за неправильного хранилища. Триггеры использовал несколько раз, только как временное решение до исправления базы. Вью оставили яркое впечатление при ра работе в банке, т.к. мало где написано, что они тратят так много ресурсов. Поэтому дешевле исправить базу, в конечном итоге, один раз, чем постоянно тратить на поддержку и хостинг. При разработке нужно считать экономический эффект продукта и если вы хотите поддерживать долго ваш продукт и чтоб вас вспоминали добрым словом, то лучше продумать дизайн заранее.
                                              +6
                                              Вам повезло, что вы не работали в телекоме или банковской сфере.
                                              Там у всех, у кого сколько-нибудь приличная нагрузка, куча хранимок.
                                                –5
                                                Я как раз работал и там и там, но повезло, что была возможность маневра. В банке рядом с их «OLAP» сделал с соответствующей бизнесу структурой и инкрементной репликацией и гонял все отчеты на ней за 5 секунд, а не на их по 40-120 минут.
                                                • UFO just landed and posted this here
                                                  0
                                                  Вам никогда не приходилось рассчитывать витрины отчетности на основании данных из АБС?
                                                  Когда в БД несколько слоев, и надо поочередно join'ить таблицы на основании, например, классификации счетов? Предлагаете гонять селект запрос на тысячу строк — а потом инсерт через какое-то внешнее приложение?
                                                    0
                                                    А можно, пожалуйста, более конкретный (учебный) пример?) хочется поиграться с такой задачкой, чисто любопытства ради (вместо разгадывания кроссвордов)
                                                  +5

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

                                              +19
                                              Я буду говорить за один только Oracle и PLSQL. Я работал с БД которые жили больше 20 лет под Oracle и вся бизнес логика была в БД. И подход, держать в БД кучу хранимок вполне имеет право на жизнь.
                                              Вот куча причин, почему в некоторых задачах стоит использовать хранимки:
                                              1. Если бизнес логика меняется, то не важно где ее менять, в бд или на сервере приложений. Ее все равно надо менять.
                                              2. Отлаживать хранимку в Oracle не многим сложнее, чем отлаживать код на Python использующий эту же БД. Вот есть функция, и разницы особо нет, где она. Дебагер в PLSQL Developer конечно уступает дебаггеру PyCharm, но это не критично.
                                              3. Средний разработчики PLSQL чаще всего намного лучше понимает работу БД. По этому решение реализованное им в хранимке может быть по производительности на порядок выше решения, сделанного Python разработчиком(по моему опыту, такое случается крайне часто)
                                              4. Хранимки близко к данным, за счет чего работают быстрее. Не раз видел случаи, когда Java + Hibernate вытаскивали две таблицы полностью на сервер приложений, чтобы сделать join и выбрать 10 строк.
                                              6. Если сервер приложений скомпрометирован, а доступ через API в БД, то злоумышленник все еще не может работать вне API
                                              7. Если у вас API реализовано в БД, вам проще организовать работу разных клиентов. Например на 1 работе у нас был клиент сделанный на delphi3 еще в конце 90х, позже появился клиент на .Net, и ближе к 2016 появился веб интерфейс. Из-за того, что вся логика в БД, добавление нового клиента было довольно простым

                                              Часто я слышу аргумент, что если много хранимого кода в БД, то переезжать на другую БД очень сложно. Но правда в том, что если вы платите за Oracle, то даже без хранимок перехать будет очень сложно. В таком случае переезд скорее всего все равно будет связан с переписванием 90% вашей кодовой базы, не зависимо от того, в Oracle она была или на сервере приложений.

                                              PS
                                              Я не говорю, что всегда логику надо реализовывать в хранимках. Я говорю, что бывают случаи, когда реализовывать логику в хранимках может быть правильным решением. И утверждать, что хранимки всегда зло и их надо избегать требует сильного доказательства.
                                                –3
                                                Я говорю, что бывают случаи, когда реализовывать логику в хранимках может быть правильным решением.

                                                Например на 1 работе у нас был клиент сделанный на delphi3 еще в конце 90х, позже появился клиент на .Net, и ближе к 2016 появился веб интерфейс. Из-за того, что вся логика в БД, добавление нового клиента было довольно простым

                                                вы правда считаете, что это именно те случаи, которые надо учитывать архитектору при проектрировании новой БД в 2020 году?
                                                  +1
                                                  Я согласен с вами, что есть в этом некоторе извращение. Но так бы я мог рассуждать, будь я архитектором(коим я не являюсь):
                                                  По моему опыту, похожий путь проходят многие АС завязанные на Oracle. Я знаю больше 10 похожих случаев в банковском и страховом серкторе. По этому в некоторых случаях этот опыт имеет смысл учесть.
                                                  Например есть опыт:
                                                  20 лет использовали одну и ту же БД, изменяя в ней хранимый для соотвествия бизнес требованиям. Это отчетная БД в одном большом банке, с предсказуемым количеством пользователей. Всю АС можно условно поделить на 3 части:
                                                  1. База данных. Содержит бизнес логику и алгоритмы построения отчетов. Дает для получения/модификации данных
                                                  2. Сервер приложений-балансировщик
                                                  3. Клиенты, которые занимаются отображением данных.

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

                                                  Предложите мне вариант лучше, я с радостью с ним познакомлюсь =)

                                                    –1
                                                    Во-первых, в вашем посте я не увидел аргумента в пользу вашей архитектуры, просто написано, что «так было». «Технологии менялись, база оставалась» — так не потому ли, что ее было невозможно поменять? Знаю кучу примеров, когда «базы менялись, логика оставалась на java» — когда люди выбирают инструмент под задачу, а не наоборот (и прекрасно живут).

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

                                                    Ну и в-третьих — прежде чем писать «а вот мы на Оракле 20 лет сидим и у нас все хорошо» — почитайте историю компании Splunk и ее клиентов в России.
                                                      +1

                                                      (микро)Сервисная архитектура: сервис на уровне приложения владеет базой данной, никто кроме него к базе доступа не имеет, вся работа через API сервиса, которых может быть несколько (скажем, SOAP, REST, GraphQL) и добавляться по мере необходимости.

                                                      • UFO just landed and posted this here
                                                        0
                                                        вы правда считаете, что это именно те случаи, которые надо учитывать архитектору при проектрировании новой БД в 2020 году?

                                                        По крайней мере это исключает такую фишку, как у банка на букву Т, когда я в разных клиентах (веб, Андроид, АйОс) вижу разную доходность по ценным бумагам.
                                                        Пусть каждый клиент сам считает свою доходность, да еще так, как удобнее программистам.
                                                          +4
                                                          Если какая-то часть логики должна работать независимо от клиента, то её действительно стоит считать на бэкенде. Но из этого никак не следует, что это нужно делать непременно в базе.
                                                            0
                                                            Никто и не говорил, что делать надо «непременно». Речь о том, что база может служить как бэкэнд и есть случаи, когда это оправдано. И утверждение, что хранимки всегда зло — не верное.
                                                              0

                                                              Для меня хранимки всегда зло :) Просто иногда это меньшее зло из остальных вариантов.

                                                          0
                                                          Да дофига таких случаев. В банках — почти вся ЦБ и налоговая отчетность. Более того, ЦБ работает с собственным форматом, разработанным ещё в 90-х, и никуда с него съезжать не собирается (((
                                                          0
                                                          Если бизнес логика меняется, то не важно где ее менять, в бд или на сервере приложений. Ее все равно надо менять.

                                                          Только в приложении ее менять проще.


                                                          Если у вас API реализовано в БД, вам проще организовать работу разных клиентов.
                                                          Из-за того, что вся логика в БД, добавление нового клиента было довольно простым

                                                          Если у вас API реализовано, вам проще организовать работу разных клиентов.
                                                          Из-за того, что вся логика в API, добавление нового клиента было довольно простым. А находится оно в БД или в отдельном приложении, это не так важно.

                                                        0
                                                        Через 10-15 лет клиент может захотеть вообще всё переписать


                                                        Или не захотеть. Что тоже реально (на моей практике типично БД живут и с 2004 и 2006 года, и до сих пор предприятие довольно и модифицирует ту старую; ну а переезд на новую стоит столь бешенных денег, что вряд ли приключится).
                                                        Ориентироваться следует «на всякий случай».

                                                        +1
                                                        А потом клиент не знает что и делать, когда 10-15 лет спустя решает переехать на другую СУБД, а там сотни недокументированных SPs на PL/SQL или T-SQL.


                                                        1) Чем эта ситуация отличается от бизнес-логики, реализованной слоем выше?
                                                        2) Что вам мешает документировать внутри PL/SQL?
                                                        3) Почему вы уверены, что при реализации бизнес-логики слоем выше — там будет документировано?
                                                        0
                                                        В Йоте в биллинке юзают оракл и там вообще всё на хранимках, никаких своих SELECT/UPDATE, и поддержанием этого отдельный отдел занимается. А ещё у них в офисе ручки (шариковые, которыми по бумаге пишут) — из бумаги))) вот такие они ребята
                                                        +5
                                                        проблема хранимок в том, что скейлить приложение намного проще, чем базу
                                                          –1
                                                          Не только скейлить, но и при необходимости переносить на другие платформы, если движка такого же на новой платформе нет. Собственно, та же проблема и у UDF.
                                                            0
                                                            то есть, чтобы 2-4-8 app серверов упирались в производительность 1 сервера БД?
                                                              +3
                                                              На деле это не так уж и больно. Ведь:
                                                              1) Множество данных можно закешировать внутри аппов, в отдельном аппе, отвечающем на запросы, или в чем-то типа редиса.
                                                              2) К серверу СУБД можно подцепить множество асинхронных ридонли реплик, и боттлнеком останется только мастер, который занимается только модифцирующими данные запросами.
                                                              3) Где-то от 80-100 ядер в одной железке начинается нелинейный рост цены при вертикальном масштабировании. Если при выборе сервера СУБД можно удержаться в зоне линейного роста, перенеся часть нагрузки на апп сервера, это значительно упрощает жизнь.
                                                                +4

                                                                1) и столкнуться с проблемой инвалидации кэша…

                                                                  +1
                                                                  не хотел возражать на все, вцелом поддерживаю (и про скейл аппа и про кеш)

                                                                  2) К серверу СУБД можно подцепить множество асинхронных ридонли реплик, и боттлнеком останется только мастер, который занимается только модифцирующими данные запросами.

                                                                  но вот тут нестыковочка, как мне видится: если вы используете репликацию, то получаете одну их 2х проблем
                                                                  а) с мастера (который делает модификации) эти запросы (sql insert/update/delete) передаются на слейвы и там всеравно исполняются — тогда противоречие с «боттлнеком останется только мастер» т.е. все реплики делают одинаковое кол-во работы, и масштабятся до определенного предела (число записей на диски)
                                                                  б) с мастера НЕ передаются sql-модификации на слейвы, чтобы выполнить ваше условие «боттлнеком останется только мастер» — действительно слейвы освобождены и можно масштабить сильней, однако данные на этих слейвах будут устаревшие (а как иначе, вы же не выполняете модификации на них), и это мало где применимо (т.е не универсально)

                                                                  что скажете?

                                                                  для масштабирования БД обычно юзают шардирование, а не репликацию, тогда каждый шард имеет срез данных (скажем 1й имеет ключи 0-1млн, 2й шард имеет 1млн-2млн и т.п) — в таком случае и БД масштабистя, и приложения при желании на нем же, т.е. на каждый шард — и данные свои и прилога своя
                                                                  … проблем с инвалидацией кеша тут конечно тоже нет (в сравнении когда много сервов БД и черт знает с какой топологией) (хотя между БД и прилогой — надо будет инвалидировать кеш разумеется, но это не проблема, а все просто)
                                                                    +3
                                                                    Read replicas нужны прежде всего для тех приложений, где чтение данных намного более часто, по сравнению с модификацией. Таких немало (всякий репортинг, например), но, разумеется, они не помогут, если у вас преимущественно идет запись или модификация данных.
                                                                    • UFO just landed and posted this here
                                                                      0

                                                                      При бинарной репликации слейвы не исполняют SQL, а тупо пишут на диск бинлог мастера потоком. Устаревания можно избежать, если делать репликацию синхронной, то есть мастер ждёт подтверждения от слейвов

                                                                        0
                                                                        > для масштабирования БД обычно юзают шардирование, а не репликацию,
                                                                        это зависит от типа нагрузки, много записи и мастер не справится — тогда да, только партиционирование, если же нагрузка преимущественно чтение — то репликации вам наверняка надолго хватит.
                                                                    –2
                                                                    Del.
                                                                  +6
                                                                  Мне это не нравится по двум причинам:

                                                                  Код приложения обычно намного проще обновить, чем схему БД.
                                                                  Все эти PL SQL-ли напоминают мне Паскаль, и они такие же уродливые.

                                                                  Так себе доводы. Прежде чем такое писать, надо смотреть что там за хранимки и что они делают. Во многих случаях использование хранимки позволяет сильно уменьшить нагрузку на БД, а так же не иметь проблем с целостностью. К примеру если вы ведете в базе баланс разумнее обновлять его хранимкой по триггеру чем заставлять всех разработчиков или писать каждый раз в баланс или писать и сопровождать специальную процедуру внутри кода.
                                                                    –1
                                                                    Прошу прощения, но я с вами не согласен.
                                                                    По моему мнению ХП — зло.
                                                                    Причем это связано не с техническими возможностями, а с организационными.
                                                                    1. Ни в одном проекте, куда я приходил работать, ХП не были в системе контроля версий. Более того никто и не собирался их туда вносить.
                                                                    2. Тестовая БД отличалась от боевой. Из-за этого ошибки в ХП отлаживались на боевой БД.
                                                                    Т.е. обновляли БД и молились чтобы все работало.
                                                                    3. Очень распространена практика кодогенерации запросов, со всеми вытекающими.
                                                                    4. Бизнес логика размазана между приложением и БД. Поиск ошибки превращается в увлекательное занятие.
                                                                    5. Никаких тестов. Даже намеков на них.

                                                                    ИМХО мой опыт говорит, хотите получить дорогую в поддержке систему с BUS-фактором 1 — используйте ХП.
                                                                      +2
                                                                      Давайте начнем с того что все вами перечисленное является последствиями не умения работы с РСУБД. Я могу заменить ХП на DDL проще говоря схему базы и получить ровно те же проблемы. Почему? Потому что в ваших доводах натурально собраны все популярные грабли и типичные ошибки при работе с базой данных

                                                                      1. Ни в одном проекте, куда я приходил работать, ХП не были в системе контроля версий. Более того никто и не собирался их туда вносить.

                                                                      B том виде как код вносится в систему контроля версий вносить ХП довольно бессмысленно. В том числе туда же бессмысленно вносить DDL. Поддерживать один файл создания базы данных в актуальном состоянии это ад. Почему? Банально у вас не прямой связи схемы внутри БД с тем что лежит у вас в системе контроля версий. Как же быть? Вариант решения проблемы есть и его файлы даже можно и нужно размещать в систему контроля версий.

                                                                      2. Тестовая БД отличалась от боевой. Из-за этого ошибки в ХП отлаживались на боевой БД.

                                                                      Тоже типичная ошибка. Возникает из-за разворачивать БД через dump долго. С определенного размера БД нужно уметь делать бинарный бекап и уметь из этого бинарного бекапа восстанавливать данные. И иметь актуальную копию. Более того у вас должно быть даже три БД. Одна для боевая, тестовая и для разработки. Как раз где нужно отлаживать и решать проблемы с ХП. Но такой подход опять требует людей умеющих работать с БД на таком уровне.

                                                                      Очень распространена практика кодогенерации запросов, со всеми вытекающими.

                                                                      В каком месте это проблема? И что из этого вытекает? Вот генерация базы из кода это да проблема. Потому что как показывает практика как только проект с таким подходом вырастает из коротких штанишек у него начинаются не хилые проблемы с производительностью. Просто в силу того что ORM это текущая абстракция. Что дает проблемы если думать что ORM сделает нам все как надо.

                                                                      Бизнес логика размазана между приложением и БД. Поиск ошибки превращается в увлекательное занятие.

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

                                                                      Никаких тестов. Даже намеков на них.

                                                                      Ну это мягко говоря не правда. Практически для любой БД есть framework для тестирования. Самый простой пример dbUnit.

                                                                      Ну а теперь немного о том как правильно работать с БД чтобы на длинном горизонте не иметь проблем с БД. Тут есть такой интересный момент что все эти практики есть там где они нужны, а именно в языке Java. И так нам потребуется:

                                                                      Database migration tool — инструмент миграции версий базы данных. Да тут появляется слово версия. В случае БД нельзя просто как с кодом положить что-то в систему контроля версий и пользоваться этим. Почему? В случае БД режим отката как правило сопряжен с довольно большими трудностями. В том числе из-за того что в ней лежат данные. По этому самым логичным способом является только аддитивный подход. Ни шагу назад. В кое-каких инструментах миграции поддерживается конечно откаты, но моя практика показывает, что легко порушить целостность данных.

                                                                      В итоге получаем
                                                                      • Таблицу с версиями внутри БД
                                                                      • Набор файлов при помощи которых можно получить рабочую пустую БД


                                                                      Да это выглядит как набор костылей по сравнению с контролем версий для кода, но по лучшего варианта увы не придумано. Зато можно узнать актуальную версию БД. В том числе и из приложения. Во многие фреймворки встраивают инструменты миграции БД. И да крайне желательно чтобы инструменты миграции могли запускаться отдельно от фреймворка и не были написаны на его языке. Это реально порочная практика используйте или sql или абстрактную схему как к примеру в liquibase.

                                                                      Тестирование БД — инструменты для тестирования БД. Сюда входят как просто тестирование БД, так и генерация тестовых данных.

                                                                      В случае java там есть три варианта
                                                                      • Flyway — миграции
                                                                      • dbUnit — тестирование БД
                                                                      • benerator — генерация тестовых данных

                                                                      ИМХО мой опыт говорит, хотите получить дорогую в поддержке систему с BUS-фактором 1 — используйте ХП.

                                                                      А мой опыт говорит, что люди часто не обладают достаточной квалификацией для работы с базами данных. Что удручает.
                                                                        0
                                                                        Для .Net все еще проще.

                                                                        Инструмент для миграций есть в EF, причем он может генерить не только классы, но и непосредственно скрипт с изменениями, включая откаты.

                                                                        Более того, я не вижу проблем с производительностью запросов при кодогенерации в том же EF Core. Достаточно следовать элементарным правилам:

                                                                        1) ToList только после Where (смешно это писать, но куча людей это забывают, особенно при работе с вычисляемыми полями)
                                                                        2) Select только тех полей, которые нужны (тоже стандартная ошибка, когда людям лень писать ViewModel и они тянут блобешники из базы в каждом запросе)
                                                                        3) AsNoTracking везде, где не нужно хранить состояние (а это практически любой запрос в Rest Api)
                                                                        4) Массовые операции добавления/изменения нужно делать балком, в идеальном случае — на сервере (используя, например, entityframework-plus.net)

                                                                        Соблюдая эти нехитрые правила можно не сталкиваться с проблемами производительности в общем случае. Да, иногда, вероятно, придется дергать хранимки из БД через ОРМ, но это всяко лучше, чем портянки SQL в коде.
                                                                          +2
                                                                          Проблема там в другом и как правило возникает уже после того как приложение более менее устоялось. Заключается она в том что если при разработке приложения идти от представлений в приложении, а не в СУБД, они могут не оптимально в нее ложится. Во многих случаях оптимальное для СУБД представление, не оптимально для приложения.
                                                                          Как итог при большом объеме данных возникают проблемы с производительностью, а так же с размером БД что может потом приводить к переделкам приложения.

                                                                          Старая добрая практика СНАЧАЛА проектируем схему базы данных, а уже потом натягиваем на нее классы работает всегда лучше чем давайте сделаем классы, а ПОТОМ положим их БД.

                                                                            +1
                                                                            Мне кажется, вы просто путаете классы приложения и модели.

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

                                                                            Проблемы обычно бывают, когда пытаются бизнес-объекты смапить напрямую в БД, вот тут да.
                                                                              0
                                                                              Я то вот не путаю :) А вот многие другие да.
                                                                              0

                                                                              Так вроде как раз ORM призваны маппить отличающиеся модели классов и таблиц друг на друга.

                                                                                0
                                                                                Нет. Это не так. См POJO в Java.
                                                                                  0

                                                                                  В смысле не так? Даже название это говорит.

                                                                                    +1
                                                                                    В прямом. На самом деле эта абстракция не работает. Что вылилось в используйте POJO при работе с ORM. Есть нормальные объекты это сервисный слой. А есть объекты модели, которые по факту просто удобная запись из БД и все.
                                                                                      0

                                                                                      POJO это полноценный объект, это не запись из БД, он имеет собственное поведение, а не просто набор сеттеров и геттеров, поля его могут отличаться от записи.


                                                                                      Собственно при использовании ORM и сеттеров особо быть не должно, только бизнес-методы, не setStatus('approved'), а aprove()


                                                                                      P.S. Всё в контексте сущности

                                                                                        0
                                                                                        Определение читали? У POJO нет никакого сложно поведения и рекомендуется не пихать туда ничего что не касается данных. А так же нет наследования. Если уж совсем упарываться то бизнес-логика живет в отдельном сервисном слое, который как раз работает уже с POJO объектами. По факту к примеру в классическом MVC того же spring у вас есть controller <-> service <-> model.
                                                                                          0

                                                                                          Определение читал. У POJO нет наследуемых методов, нет реализации каких-то сторонних интерфейсов, но методы могут быть сколь угодно сложными, пока они относятся к отвественности этого объекта (а он может быть, например, корнем агрегата). И бизнес-логика в нём вполне может быть. Она размазана между объектами сущностей и сервисами доменного уровня. В сервисы выносится то, что сложно отнести к одной сущности, где сложное их взаимодействие. Если какой-то метод сервиса работает с данными только одного объекта сущности, то он должен быть в этой сущности.

                                                                                            0
                                                                                            Не стоит так делать. В уровень POJO лучше всего класть валидацию и прочие вещи относящиеся к данным. Например умолчания. Класть еще и бизнес-логику мотивируя ну можно же так себе затея. Это приводит к размазыванию функционала и постоянной телепатии где же там то или иное. Стоит именно четко делить где что. Именно в этом идея POJO именно по этому там наследования и интерфейсов. У меня по факту в POJO объектах кроме определений сущности и схем сериализации, а также болерплейта в виде геттеров сеттеров ничего не бывало. Вся логика на уровне сервисного слоя всегда.
                                                                                              0

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


                                                                                              Идея POJO в качестве моделей сущности для меня сводится к тому, что слой бизнес-логики не имеет никаких внешних зависимостей, кроме библиотек общего назначения. При этом у него низкая связанность и высокая связность.

                                                                            0
                                                                            1) Нет контроля версий — нет разработки. Привет из 80-х.
                                                                            Т.к. нет контроля версий, то закоментированные куски кода могут находиться в ХП десятилетиями (были примеры)

                                                                            2) Никто, никому ничего не должен. Программист не администратор, у него даже прав может не быть на создание и восстановление бекапов. Так что идут по пути наименьшего сопротивления — Работают на прямую в проде.

                                                                            3) Ну вообще-то куча уязвимостей через SQL инъекции, это как раз из-за кодогенерации в приложении или ХП. Ладно пофиг на бещопастность. Но чтобы разобраться в ХП, которая генерит запрос в зависимости от данных в сгенерированном запросе.
                                                                            В обще-то еще в 90-е поняли, что самомодифицирующиеся программы — ЗЛО.
                                                                            Например было интересно разбирать программы на FoxPro, не говоря уже о Clipper.
                                                                            Т.к. там как раз в программе написать программу.

                                                                            4) «Если коготок увяз, всей птичке пропасть». Как только написал первую ХП, то неминуемо часть бизнес-логики если не вся переезжает в ХП. Потом кто это делал уволянется. И никто ничего в данном проекте не понимает. Тестов нет, версий нет. От трети, до половины ХП состоят из комментариев с кусками кода.

                                                                            5) Для обычных ЯП тесты пишут мало. Хотя для этого сделаны все удобства. А в БД это очень не удобно. Поэтому я не видел, чтобы для БД были тесты.

                                                                            6) Понятно, что быть «богатым и здоровым лучше, чем бедным и больным». Но опять же проблема в том, что разработка в БД застряла в 80-х.
                                                                            Если говорить о Java, то мне проще вообще «на заморачиваться с БД».
                                                                            Выделить в отдельный слой. Обложить spring-овыми тестами. И работать только через Spring-Data-Jpa.

                                                                            7) Мой опыт говорит, что законы Мерфи работают в программировании. «Если что-то может быть сделано не правильно, то со 100% вероятностью это будет сделано не правильно». Надеяться, что легаси проект делали только люди с достаточной квалификацией, это наивно.
                                                                              0
                                                                              Ну вообще я уже ответил
                                                                              habr.com/ru/post/487622/?reply_to=21263784#comment_21259204

                                                                              Но давайте еще раз пройдемся по
                                                                              1) Нет контроля версий — нет разработки. Привет из 80-х

                                                                              Решается через инструменты миграции базы данных. Единственная проблема, нормально это делать можно только через добавление. Из минусов это приводит к накоплению изменений. Увы с базами данных контроль версий сложно придумать, метаданные завязаны на данные, чего например нет в коде.

                                                                              Никто, никому ничего не должен. Программист не администратор, у него даже прав может не быть на создание и восстановление бекапов. Так что идут по пути
                                                                              наименьшего сопротивления — Работают на прямую в проде.

                                                                              По той же логике можно не использовать системы контроля версий и править код прямо на проде. И у многих именно так происходит. И более того экстренные фиксы МОГУТ вноситься прямо на прод минуя к примеру CI. Это организационный вопрос. Почему мы с кодом умеем работать при помощи контроля версий CI и прочего, а в случае СУБД просто ленимся? Причем инструменты то есть.

                                                                              Ну вообще-то куча уязвимостей через SQL инъекции, это как раз из-за кодогенерации в приложении или ХП. Ладно пофиг на бещопастность. Но чтобы разобраться в ХП, которая генерит запрос в зависимости от данных в сгенерированном запросе.

                                                                              Для того чтобы у вас были уязвимости через инъекции надо не проверять входящие параметры. И да через ХП вы НЕ СМОЖЕТЕ сделать инъекцию, как и в случае и через prepared statements оба этих инструмента ждут на вход вполне четкие параметры. И нормальные разработчики никогда не используют eval из склеенных строк которые приходят в ХП. То что далее рассказываете можно встретить и в любом ЯП и это называется плохой код.

                                                                              Как только написал первую ХП, то неминуемо часть бизнес-логики если не вся переезжает в ХП. Потом кто это делал уволянется. И никто ничего в данном проекте не понимает. Тестов нет, версий нет. От трети, до половины ХП состоят из комментариев с кусками кода.

                                                                              Проблема высосана из пальца. Не кладите в ХП то что не нужно. Далее фактор автобуса работает и в случае обычного кода и особенно в случае самописных фреймворков. Никакой разницы.

                                                                              Для обычных ЯП тесты пишут мало. Хотя для этого сделаны все удобства. А в БД это очень не удобно. Поэтому я не видел, чтобы для БД были тесты.

                                                                              Но они есть. И там где надо их пишут. И более того инструментарий есть.

                                                                              Если говорить о Java, то мне проще вообще «на заморачиваться с БД».
                                                                              Выделить в отдельный слой. Обложить spring-овыми тестами. И работать только
                                                                              через Spring-Data-Jpa.

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

                                                                              А вам бы я рекомендовал просто прочитать методичку по СУБД. На том же citforum была весьма хорошая.
                                                                                0
                                                                                1. Есть контроль версий. Надо просто потратить время на описание и отладку процесса. Я описывал, как это сделано у нас в команде в комментариях 21258116 и 21262602
                                                                                2. Организационная проблема, а не проблема СУБД
                                                                                3. Проблема квалификации специалиста. Не пиши хреновые ХП и проблемы не будет
                                                                                4. Опять организационная проблема. Тесты надо писать, контроль версий использовать, с техдолгом бороться, и проблемы не будет
                                                                                5. «Неудобно» не значит «невозможно». Лень и недостаточная квалификация специалиста — не проблема СУБД
                                                                                6. Есть проекты, где такой подход оправдан, а есть такие, где он невозможен
                                                                          +1
                                                                          Люблю естественные ключи. В случае с email имхо пример не слишком удачный изначально, закладываться на уникальность email.
                                                                            0
                                                                            В этом примере неудачно выделен признак, идентифицирующий пользователя. Адрес email идентифицирует… адрес email. Подходящим естественным ключом для пользователя мог бы стать его login. Нет в системе понятия логина — значит, делаем суррогатный ключ.

                                                                            Это примерно та же ошибка, когда для физлица в качестве естественного ключа принимают номер паспорта. А вот в таблице паспортов номер паспорта был бы вполне уместным естественным ключом.
                                                                              0
                                                                              А вот в таблице паспортов номер паспорта был бы вполне уместным естественным ключом.
                                                                              У человека может быть несколько паспортов и номера могут совпасть в разных странах, поэтому это не лучший пример тоже.
                                                                                +5
                                                                                Подходящим естественным ключом для пользователя мог бы стать его login.
                                                                                Ага, а потом перед нами встает задача по реализации смены логина для пользователя…
                                                                                  +2
                                                                                  И мы эту задачу решаем…
                                                                                  1) каскадным обновлением внешних ключей (фразу ON UPDATE CASCADE никто не отменял)
                                                                                  2) понимаем, что такая задача когда-нибудь возникнет, и не делаем login первичным ключом, а опять же вводим суррогатный ключ.
                                                                                    0
                                                                                    Любые значащие данные, используемые в качестве первичного ключа, могут потребовать изменений ввиду внешних факторов.

                                                                                    Поэтому лично я предпочитаю использовать искусственный внешний ключ (до тех пор, пока нет весомой причины его не использовать). Заодно это снимает проблему правильного выбора естественного ключа :)
                                                                                      –1
                                                                                      Бездумное введение суррогатных ключей приводит к противоречивости данных.
                                                                                      Пример с логином.
                                                                                      Ввели суррогатный ключ, забыли поставить уникальность на login.
                                                                                      Вот два пользователя с одинаковыми логинами.

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

                                                                                      P.S. Смена логина — это новый пользователь.
                                                                                      И это процедура скорее исключение, чем правило.
                                                                                      Там должен быть свой бизнес процесс, и скорее всего с участием оператора.
                                                                                        0
                                                                                        К противоречивости данных тут привел не суррогатный ключ, а безалаберность.
                                                                                        Если некто забывает такие вещи, естественный первичный ключ его не спасет: 1) вообще-то PRIMARY KEY тоже можно забыть добавить; 2) у сущности вполне себе может быть несколько уникальных (комбинаций) полей, на каждое по первичному ключу не создашь. Это не тот случай, когда можно себе позволить «забывать».
                                                                                          0
                                                                                          Ввели суррогатный ключ, забыли поставить уникальность на login.
                                                                                          Если забыли поставить уникальность на то поле, которое должно быть уникальным, то причина в этом и только в этом.

                                                                                          Как в вашем подходе дать пользователю вохможность входить по логину ИЛИ по email ИЛИ по номеру телефона (на усмотрение пользователя), и при этом сохранить требование на уникальность каждого из этих аттрибутов?

                                                                                          P.S. Смена логина — это новый пользователь.
                                                                                          И это процедура скорее исключение, чем правило.
                                                                                          Нет. Встречал такое, например, во многих ИБ.
                                                                                            0
                                                                                            Ввели суррогатный ключ, забыли поставить уникальность на login.

                                                                                            Не забывайте.
                                                                                            Имхо, ключи на уникальность полей/сочетаний полей — одно из первейших дел
                                                                                          +3
                                                                                          1) каскадным обновлением внешних ключей (фразу ON UPDATE CASCADE никто не отменял)

                                                                                          Так себе решение… Часто данные растекаются за пределы оперативной базы. Логи хранятся в elastic, например, обращения в техподдержку в тикет-системе, может быть отдельная СУБД с денормализированными данными и колоночным хранением строго под отчеты, если объем данных того требует.
                                                                                      +1
                                                                                      Тогда в каком случае естественный ключ действительно хорош? В документах типа паспорта?
                                                                                        –1
                                                                                        Там, где такой код-ключ исходно предусмотрен для объекта и в таблице в базе сосредоточены параметры только для классифицируемого объекта.
                                                                                        Например для книг есть ISBN.
                                                                                          +2
                                                                                          Для выпущенных книг есть ISBN, для невыпущенных — нет. Если книга ещё только в проекте, то мы не сможем начать работу над ней.
                                                                                          Вообще ни разу не видел чтоб где-то прокатывал естественный ключ. Вру, видел в одной системе, там пользователь задаёт это значение, но раз и навсегда — больше он ничего с ним не может поделать (хотя потом возникли ситуации, когда всё-таки надо было). Но если ключ может меняться, то это уже плохой вариант для ключа.
                                                                                            0
                                                                                            С ISBN,DOI и прочих ISSN'ах проблема на стадии продакшена книги\журнала. ID может не быть или даже, при особо кривых сценариях, он может поменяться в процессе
                                                                                              0
                                                                                              С ISBN еще можно вспомнить глобальный переход от ISBN-10 к ISBN-13.
                                                                                            +2
                                                                                            Тогда в каком случае естественный ключ действительно хорош? В документах типа паспорта?

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

                                                                                            –2
                                                                                            Соглашусь. Суррогатные ключи в большинстве случаев зло. Т.е. для таблицы должен быть выбран нормальный естественный первичный ключ — это вообще как бы основы проектирования схемы БД(если это e-mail то он будет отображаться во всех интерфейсных формах, к примеру формах где надо выбирать пользователя, фильтровать по пользователю и пр..). При этом база будет гарантировать его уникальность. Другое дело что его надо выбирать осознано.
                                                                                            В том же примере когда для пользователя выбирается суррогатный ключ у вас появляется сразу куча проблем — во всех запросах связанных с интерфейсом где присутствует пользователь нужно джойнить таблицу пользователей, проблемы с сортировками(обычно для первичного ключа хотят видеть в каком-то упорядоченном виде), невозможность настроить нормальные индексы(т.е. типичный вопрос пользователя — покажи мне заказы от пользователя за определенную дату — в случае с естественным ключем вы просто сделаете индекс(пользователь, дата), в случае с суррогатным, ничего не выйдет, так как поля в разных таблицах), нужно все равно вводить уникальность на этот e-mail.
                                                                                            Переделывать при изменении первичного ключа все равно надо много, с этим ничего не поделаешь
                                                                                              +1
                                                                                              В том же примере когда для пользователя выбирается суррогатный ключ у вас появляется сразу куча проблем — во всех запросах связанных с интерфейсом где присутствует пользователь нужно джойнить таблицу пользователей
                                                                                              Не обязательно джойнить — есть и другие варианты. И это не проблема, это универсальный работающий механизм.

                                                                                              Представьте себе, что в вашем случае клиент просит заменить во всех интерфейсных формах email пользователя на его логин (телефон, etc). Каждый раз ключ менять? А если клиент потом просит запилить A/B тестирование для двух вариантов одновременно? :)

                                                                                              Имхо зло — это привязка структуры данных к представлению, выбранному для них в данный момент.

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

                                                                                              Просто индекс для заказов будет (id_пользователя, дата). При этом если пользователь где-то выбирается в интерфейсе (например, из списка пользователей) — значит, выборка пользователей из базы перед этим все равно делается и просто ее нужно делать вместе с id (а в интерфесе, грубо, будет &ltoption value="user.id">user.email&lt/option> вместо &ltoption value="user.email">user.email&lt/option>). Если же email вводится вручную — нужно просто найти пользователя в базе перед тем, как делать выборку заказов. Впрочем, при использовании email в качестве первичного ключа это также имеет смысл сделать, например, хотя бы для разграничения результатов «нет пользователя с таким email» и «нет заказов у этого пользователя».
                                                                                                +2
                                                                                                Не соглашусь. Самый правильный вариант — суррогатный первичный + естественный уникальный ключи. Особенно на таблицах с кучей зависимостей, (как таблица пользователей из вашего примера). Потому что, когда ваш естественный ключ внезапно станет не уникальным (или изменяемым), проще пересоздать уникальный ключ в одной таблице, чем: а) добавлять колонку во все зависимости б) заполнять ее б) пересоздавать внешние ключи.
                                                                                                  +2
                                                                                                  Т.е. для таблицы должен быть выбран нормальный естественный первичный ключ — это вообще как бы основы проектирования схемы БД

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


                                                                                                  Уникальная характеристика может быть только у информационных объектов, которые являются обобщением сгруппированных по этой характеристике естественных объектов, либо если это искусственный ключ, не являющийся собственной характеристикой объекта. Пример первого — номенклатуры, пример второго — инвентарные номера.

                                                                                                    –1
                                                                                                    Современные принципы разработки говорят что мы не делаем того что не нужно, т.е. это опять же не очень правильно при дизайне руководствоваться какими-то будущими требованиями
                                                                                                    Возьмем к примеру хабр — ник пользователя(DenisTrunin, michael_vostrikov) — это вполне уникальная вещь, менять его вряд-ли возможно и добавив его во все таблицы мы получим более простую схему и более простые запросы. Т.е. во многих случаях не надо будет вообще лезть в таблицу пользователей, чтобы получить то что надо из других таблиц(посты по пользователю, топ активных пользователей и пр..). т.е. во многих случаях будет на порядок больше производительность(за счет того что не будет джойнится таблица пользователей, для многих запросов будет идти просто ник)
                                                                                                    Согласен что мы теряем возможность быстрой смены ника, то поскольку это исключение, можно к примеру иметь скриптик, который по связям это сделает
                                                                                                    К недостаткам можно отнести и то что таблицы могут побольше занимать места(но экономить байты сейчас не принятно)
                                                                                                      0
                                                                                                      Как-то работал с SQLite базой скайпа и генерировал ссылки для обработки клиентом скайпа. И там прикольный костыль был. Разработчики скайп тоже решили, что
                                                                                                      ник пользователя(DenisTrunin, michael_vostrikov) — это вполне уникальная вещь, менять его вряд-ли возможно и добавив его во все таблицы мы получим более простую схему и более простые запросы.

                                                                                                      Но потом к ним пришли маркетологи, и сказали, мол, запилите регу через фейсбук. И тогда у некоторых юзеров первичный ключ стал выглядеть, как facebook:vasya.pupkin, в то время, как у всех «нормальных» пользователей было просто vasya.pupkin. Ссылки же стали выглядеть, как
                                                                                                      <a href="skype:facebook:vasya.pupkin?chat">Start Chat</a>

                                                                                                      Вроде и нечего такого, и любой школьник, сдавший на «хорошо» информатику, распарсит такое без проблем. Но, зато я стал отлично понимать разрабов, почему они так яростно блокируют сервером старые версии. Учитывая плотность костылей (а их еще вагон, связанных с переходом P2P -> cloud чаты, например, или с поддержкой форматирования, которое выпилили, но не совсем), задача обеспечить хоть какую-то работоспособность старых версий, и при этом не вызывать взрывы пуканов, реально нетривиальная.
                                                                                                        0
                                                                                                        Т.е. вы думаете что если бы вместо
                                                                                                        <ahref="skype:facebook:vasya.pupkin?chat">Start Chat было бы
                                                                                                        <ahref="skype:1234:45667?chat">Start Chat где эти индентификаторы ссылаются на другие таблицы и чтобы понять что за пользователь надо лезть в эти таблицы это было бы лучше?
                                                                                                        по мне так во многих случаях такое имя позволит не обращаться к другим таблицам и будет достаточным, т.е. будет более удобно
                                                                                                          0
                                                                                                          Думаю, что ссылки еще куда не шло (отдавать во вне внутренний ключ, — тоже сомнительное решение), но вот тащить в схему SQLite такой мусор, — совсем не комильфо. Тот же телеграм имеет user id, который, правда, многие владельцы аккаунтов не знают, ведь он только при общении с телеграм ботами виден для бота. И такое решение выходит куда гибче.
                                                                                                        +1
                                                                                                        Возьмем к примеру хабр — ник пользователя(DenisTrunin, michael_vostrikov) — это вполне уникальная вещь

                                                                                                        Ага, специально задуманная как уникальная. Это не естественный ключ, это и есть суррогатный ключ. Захочет человек с таким же именем взять такой же ник, а нельзя. И единственное препятствие для этого — это ограничение в движке Хабра, а не какое-то свойство предметной области.


                                                                                                        менять его вряд-ли возможно

                                                                                                        Возможно, через техподдержку. А в вашем варианте это как раз будет проблематично.


                                                                                                        и добавив его во все таблицы мы получим более простую схему и более простые запросы

                                                                                                        Они ничем не проще, вместо поля user_id будет поле user_nick.
                                                                                                        В запросах будет сравнение строк вместо сравнения чисел, значит производительность будет меньше.
                                                                                                        И различные GDPR будет сложнее соблюдать. Пришел к вам запрос от Васи Пупкина "удалите все мои персональные данные", а у вас везде в логах ники пользователей, содержащие имя и фамилию.


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

                                                                                                        Вам всегда надо будет лезть в таблицу пользователей, если только у вас не простенький форум. Обычное имя, аватарка, дата последнего логина, карма/рейтинг.


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

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


                                                                                                        За счет того что не будет джойниться таблица пользователей, для многих запросов будет идти просто id. Тут вообще никакой разницы нет. Строка с пользователем достается по id или по нику 1 раз за время обработки веб-запроса, и дальше в запросах можно использовать любое поле из нее. Такие запросы как раз неплохо кешируются.

                                                                                                          0
                                                                                                          Это не естественный ключ, это и есть суррогатный ключ. Захочет человек с таким же именем взять такой же ник, а нельзя.

                                                                                                          Что-то вы запутались, ник никак не может быть суррогатным ключем по определению(это обычно GUID или автоинкрементное поле). Ник это как раз естественный ключ, которые автор статьи предлагает не использовать

                                                                                                          Суррога́тный ключ — понятие теории реляционных баз данных.
                                                                                                          Это дополнительное служебное поле, добавленное к уже имеющимся информационным полям таблицы, единственное предназначение которого — служить первичным ключом. Значение этого поля не образуется на основе каких-либо других данных из БД, а генерируется искусственно.

                                                                                                          И различные GDPR будет сложнее соблюдать. Пришел к вам запрос от Васи Пупкина «удалите все мои персональные данные», а у вас везде в логах ники пользователей, содержащие имя и фамилию.

                                                                                                          Интерестный момент конечно, гугл говорит что нет:)
                                                                                                          Ник без других данных не считается персональными данными

                                                                                                          Вам всегда надо будет лезть в таблицу пользователей, если только у вас не простенький форум. Обычное имя, аватарка, дата последнего логина, карма/рейтинг.

                                                                                                          Ну вот как правило нет — т.е. возьмем нашу дисскуссию в хабр, детальная информация показывается только при наведении мышки, все остальное — это данные таблицы комментов
                                                                                                          За счет того что не будет джойниться таблица пользователей, для многих запросов будет идти просто id. Тут вообще никакой разницы нет. Строка с пользователем достается по id или по нику 1 раз за время обработки веб-запроса, и дальше в запросах можно использовать любое поле из нее. Такие запросы как раз неплохо кешируются.

                                                                                                          А если сортировка? Т.е. задача — отобразить комменты для поста отсортированные по нику пользователя, пока всех не достанете, отсортировать не сможете. ну т.е. понятно что где-то это будет 1 доп небольшой запрос, а где-то могут быть вполне и большие запросы(т.е. вынося ник по которому строятся запросы в отдельную таблицу вы теряете возможность создавать индексы по этому полю, а это иногда нужно)
                                                                                                            +1
                                                                                                            Что-то вы запутались, ник никак не может быть суррогатным ключем по определению(это обычно GUID или автоинкрементное поле)
                                                                                                            Значение этого поля не образуется на основе каких-либо других данных из БД, а генерируется искусственно.

                                                                                                            Я не запутался, я говорю о том, что все поля, которые считаются естественными первичными ключами, это точно такие же суррогатные id, значения которых генерируются искусственно.


                                                                                                            Интерестный момент конечно, гугл говорит что нет:)

                                                                                                            Я думаю, надо не в гугле смотреть. а в самом GDPR.


                                                                                                            https://gdpr-info.eu/art-4-gdpr/


                                                                                                            "‘personal data’ means any information relating to an identified or identifiable natural person (‘data subject’); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person;"


                                                                                                            возьмем нашу дисскуссию в хабр, детальная информация показывается только при наведении мышки, все остальное — это данные таблицы комментов

                                                                                                            Аватарка показывается без наведения мышки, и это не данные таблицы комментов.


                                                                                                            А если сортировка? Т.е. задача — отобразить комменты для поста отсортированные по нику пользователя, пока всех не достанете, отсортировать не сможете.

                                                                                                            Сортировка да, с джойнами делается.
                                                                                                            Задача — отобразить комменты для поста отсортированные по имени пользователя, пока всех не достанете, отсортировать не сможете. И вот сортировка по имени встречается гораздо чаще сортировки по никам или по email.


                                                                                                            т.е. вынося ник по которому строятся запросы в отдельную таблицу вы теряете возможность создавать индексы по этому полю

                                                                                                            Не теряется никакая возможность, вам же уже написали — делается индекс по user_id, хоть простой, хоть составной. А для поиска по тексту есть специальные средства, не надо для этого использовать первичные ключи.

                                                                                                              0
                                                                                                              Я думаю, надо не в гугле смотреть. а в самом GDPR.

                                                                                                              забавно. Похоже я искал по российскому определению персональных данных. Но не суть, т.е. если такое нужно(или нужно давать менять ник), тогда да, надо использовать внутреннее ID, получая более сложную систему. Но идея то автора что он предлагает это использовать всегда, жертвуя по сути удобством и производительностью
                                                                                                                +1

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

                                                                                                                  0
                                                                                                                  Ага удобнее :) только пользователи недовольны когда отображение списка из 10 полей тормозит из-за того что используется 10 джойнов(вместо простого запроса по таблице). Зачем хранить абстрактное значение если можно хранить естественный код.
                                                                                                                    0

                                                                                                                    10 джойнов на тех объемах данных, которые выводятся пользователю, не должно быть проблемой для БД. Тем более что раз у вас получается простой запрос, значит из каждой таблицы используется по одному полю, скорее всего названия, это похоже на какие-то справочники типов или категорий, где записей обычно в пределах 1000. Если хотите, можете выложить запрос и структуру таблиц (без данных) куда-нибудь на GitHub с замененными названиями таблиц и полей на выдуманные, попробуем проанализировать, потом удалите.


                                                                                                                    Зачем хранить абстрактное значение если можно хранить естественный код.

                                                                                                                    Потому что естественный код это тоже абстрактное значение, просто придуманное кем-то другим.

                                                                                                                      0
                                                                                                                      Потому что естественный код это тоже абстрактное значение, просто придуманное кем-то другим.
                                                                                                                      Тут важное замечание — значит ли для пользователя что-то этот код. Если значит, его как раз можно использовать. Если это абстрактный GUID, то для чего он нужен.
                                                                                                                      Пример можно привести очень простой — строки заказа где есть код товара. Обеспечить вывод первых 10 строк(к примеру размер страницы) упорядоченных по коду товара. Или фильтрацию по первым буквам кода товара. Сравните накладные расходы когда этот код товара хранится прямо в таблице строк, или в таблице строк хранится GUID и чтобы достать код нужно куда-то лезть.
                                                                                                                      В первом случае простой индекс(код заказа, код товара) решит все проблемы, во втором вам каждый раз придется сканировать весь состав заказа
                                                                                                                        0

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


                                                                                                                        Пример можно привести очень простой — строки заказа где есть код товара.

                                                                                                                        Можно использовать числовой ID как код товара.
                                                                                                                        Если же использовать буквенные коды это стороннее требование, можно сделать джойн, или 2 запроса, или отдельный индекс для полнотекстового поиска. Как я уже сказал, это решается другими средствами, а не первичными ключами.


                                                                                                                        или в таблице строк хранится GUID и чтобы достать код нужно куда-то лезть.

                                                                                                                        Так вам все равно надо туда лезть, чтобы вывести человекочитаемое название товара.

                                                                                                                          0
                                                                                                                          Так вам все равно надо туда лезть, чтобы вывести человекочитаемое название товара.

                                                                                                                          Ну если коды выбраны правильно и пользователи их знаю, то может и надо не всегда
                                                                                                                          Ну или еще более простой случай — код единицы измерения в этой же таблице. что лучше использовать в качестве ключа — нормальный код строка 10(штуки, короба, пары) или числовые ID?
                                                                                                                          Понятно что все можно сделать и со всем работать, создать вью например, потом вью на вью и т.п. но зачем осознано усложнять схему бд
                                                                                                                            0

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


                                                                                                                            создать вью например, потом вью на вью

                                                                                                                            Я не предлагал создавать вью, я перечислил другие варианты. Самый простой из них — сделать 2 запроса, первый с LIKE по коду и возвращает список id, второй с IN.
                                                                                                                            Еще можно подумать, зачем вообще пользователи ищут заказы по буквам из кода товара и сделать отдельный фильтр с выпадающим списком в интерфейсе. Пользователь ввел буквы в фильтре, отметил галочками нужные коды товаров, программа взяла id отмеченных кодов и нашла заказы. Никакого усложнения и никаких джойнов.


                                                                                                                            код единицы измерения в этой же таблице. что лучше использовать в качестве ключа — нормальный код строка 10(штуки, короба, пары) или числовые ID?

                                                                                                                            Лучше для чего?
                                                                                                                            Лучше использовать числовые ID, потому что логически тут вообще никакой разницы нет, в обоих случаях фиксированное перечисление, доступное полностью из любой точки программы, а технически числовые ID обрабатываются быстрее. И это мы еще про переводы на другой язык не начинали.

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

                                                                                                                              Если у вас появляется такое требование, то тогда да, от ID вам не уйти, тогда его придется добавлять плюс продумывать и писать доп код для разных ускорений. Т.е. я с этим не спорю. Вопрос — зачем так делать изначально и по умолчанию. Вы просто получите меньшую производительность и получите большую сложность схемы БД
                                                                                                                                0
                                                                                                                                я вам привел несколько примеров когда это совсем не быстрее(сортировки, джойны)

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


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


                                                                                                                                Вопрос — зачем так делать изначально и по умолчанию. Вы просто получите меньшую производительность и получите большую сложность схемы БД

                                                                                                                                Затем, что мы получаем большую производительность и меньшую сложность схемы БД. Я же приводил примеры, и даже предложил обсудить ваш конкретный с 10 джойнами, который тормозит.
                                                                                                                                И затем, чтобы если такое требование придет, не переделывать всю систему.

                                                                                                                              +1
                                                                                                                              нормальный код строка 10(штуки, короба, пары) или числовые ID?

                                                                                                                              enum (либо на уровне БД, либо на уровне ORM с маппингом на числовые ID). Иначе вы либо сталкиваетесь с тем, что у вас для каких-то товаров вписано «шутки» вместо «штуки» ввиду ошибки человека, который вносил данные, либо придумываете свои костыли для реализации функционала по предотвращению подобных ошибок, присутствующего в enum.
                                                                                                                                0
                                                                                                                                Ну мы тут обсуждаем более сложный случай когда это отдельная таблица(в которой есть уникальное текстовое поле «Код единицы измерения», доп поля типа кратность, склонениния и пр..). Костылей никаких не будет, вы в любом случае делаете связь между таблицами. Вопрос как лучше ссылаться на такую таблицу — по «Коду единицы измерения» или надо в нее добавлять еще одно уникальное поле(GUID или ID) и ссылаться на него
                                                                                                                                  0
                                                                                                                                  Кратность? Зачем ее выносить отдельно от товара?
                                                                                                                                  И я не понял — у вас в этой таблице будут записи «единицы: штуки, кратность: 10» и «единицы: штуки, кратность: 100»? Как тогда «штуки» могут быть первичным ключом? Вам придется составной ключ делать же. И тащить оба поля в товар. Либо таки сделать id и не мучаться.

                                                                                                                                  А уж склонения в БД… для каждого языка добавлять? В одну таблицу вперемешку с данными, относящимися к бизнес-логике приложения? Серьезно?

                                                                                                                                  Выглядит так, что вы сову на глобус натягиваете. И хотите ее прибить гвоздями, чтобы ни шага влево и ни шага вправо от выбранной изначально концепции и выбранного изначально представления данных.
                                                                                                                                    0
                                                                                                                                    И я не понял — у вас в этой таблице будут записи «единицы: штуки, кратность: 10» и «единицы: штуки, кратность: 100»? Как тогда «штуки» могут быть первичным ключом?

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

                                                                                                                                      И если нам действительно необходима отдельная таблица, то мы можем либо сделать id и получить универсальное гибкое решение, которое легко адаптируется под изменение требований, либо прибить все гвоздями к текущей формулировке задачи, возможно, получив какой-то выигрыш в производительности (неизвестно, насколько в действительности нам необходимый) для каких-то частных сценариев ценой отедльного кастомного решения под этот сценарий, но получив кучу боли и необходимость гораздо более серъезных переделок, когда требования изменяются.
                                                                                                                                        0
                                                                                                                                        Ну вообще википедия довольно хорошо описывает недостатики суррогатных ключей, так что лепить их всегда по умолчанию я бы не назвал лучшей стратегией. из практики самое плохое из этого списка это
                                                                                                                                        • Склоняет администратора пропустить нормализацию.
                                                                                                                                        • Вопросы оптимизации

                                                                                                                                        ru.wikipedia.org/wiki/%D0%A1%D1%83%D1%80%D1%80%D0%BE%D0%B3%D0%B0%D1%82%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D1%8E%D1%87
                                                                                                                                          0
                                                                                                                                          Так вы про практику или про Википедию? :)

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

                                                                                                                                          А когда вы начинаете заниматься оптимизацией отдельных мест в ущерб гибкости и универсальности ДО того, как у вас возникают проблемы с производительностью именно в этом месте (или до того, как становится очевидно, что они непременно возникнут именно в этом месте) — это ни что иное как преждевременная оптимизация. Которая, как известно, is the root of all evil (or at least most of it) :) Применительно к данному случаю, хотя бы потому, что, как вам выше указывали, там, где все равно придется использовать джойны, на естественных строковых ключах это может быть медленнее, и ваша «оптимизация» в итоге может выйти вам же боком в плане производительности. Либо в плане требуемого места (для базы, для бэкапов) из-за того, что внешние ключи в огромной таблице будут в несколько раз больше, чем они могли бы быть.
                                                                                                                                            0
                                                                                                                                            Суррогатный ключ нормализации не мешает и это похоже на какую-то отговорку админа.
                                                                                                                                            Регулярно наблюдаю такое. Люди просто делают суррогатный ключ для справочников и забывают(или забивают) что надо еще сделать естественный. Если добирается до прода и пользователи по ошибке вбивают дублированные данные исправлять это долго и сложно
                                                                                                                                            По поводу оптимизации — я как бы согласен что сурогатный ключ дает больше возможностей и с помощью него можно реализовать более сложную схему данных. Но его использование — это как раз и будет преждевременная оптимизация, которой как вы правильно заметили заниматься не надо
                                                                                                                                              +1
                                                                                                                                              Люди просто делают суррогатный ключ для справочников и забывают(или забивают) что надо еще сделать естественный
                                                                                                                                              Ну так проблема в том что забивают, а не в том, что делают суррогатный ключ. Кстати, а можно пример реального случая, когда вот прямо было нужно сделать и суррогатный ключ и естественный, но его не сделали?

                                                                                                                                              я как бы согласен что сурогатный ключ дает больше возможностей и с помощью него можно реализовать более сложную схему данных. Но его использование — это как раз и будет преждевременная оптимизация
                                                                                                                                              Не более сложную (или, как минимум, не всегда более сложную) а более гибкую. А структура данных часто становится даже проще в некотором смысле для понимания, если у каждой сущности есть первичный ключ id, и все внешние ключи имеют вид сущность_id.

                                                                                                                                              И про преждевременность выбора более гибкого и дающего больше возможностей варианта как-то немного странно слышать от человека, который парой комментариев ниже писал про эджайл. Тот самый эджайл, одна из четырех основных ценностей которого — responding to change over following a plan. Так вот когда вы выбираете естественный первичный ключ — вы обрекаете себя на этот самый following a plan. Ради того, чтобы просто не было джойнов в каких-то отдельных случаях (неизвестно, насколько часто используемых и насколько важных с точки зрения производительности). При том что изменения, которые не соответствуют первоначальному плану, потребуют существенных трудозатрат. И при том, что время процессора, которой джойнит таблицы, сегодня зачастую весьма дешево, а вот труд разработчика — дорог.
                                                                                                                                                0
                                                                                                                                                Кстати, а можно пример реального случая, когда вот прямо было нужно сделать и суррогатный ключ и естественный, но его не сделали?

                                                                                                                                                Да любой. Простая таблица Код группы клиентов(строка10), Название группы. Во всех интерфейсных полях пользователь видит этот код. Также этот код подразумевается уникальным. Для связи вводят еще одно поле — какой-нибудь ID, объявляют его первичным ключем и связывают по нему. По коду забывают построить индекс. Ну т.е. более правильно в этом случае объявить Код первичным ключем, его же использовать для связи
                                                                                                                                                А структура данных часто становится даже проще в некотором смысле для понимания, если у каждой сущности есть первичный ключ id

                                                                                                                                                вот как раз и нет(об этом и написано в википедии). т.е. при такой схеме вы видете какой-то непонятный ID и даже не понимаете схему и какие поля уникальные. Например в примере выше кто-то может решить что уникальное имя и пр… т.е. такая схема повышает сложность и вероятность ошибки
                                                                                                                                                Согласен с вашим последним параграфом, но именно поэтому если возможно и надо использовать естественные ключи
                                                                                                                                                  +1
                                                                                                                                                  По коду забывают построить индекс.
                                                                                                                                                  Ну так ключи тут ни при чем. Просто забыли сделать индекс по полю, по которому его нужно было сделать.

                                                                                                                                                  Ну т.е. более правильно в этом случае объявить Код первичным ключем, его же использовать для связи
                                                                                                                                                  А Код не будет меняться? А точно не будет? А если все-таки будет? А он числовой или строковый? А длинный? А зачем нам вообще название группы, если везде отображается только код, а не название? Или где-то название тоже отображается? А если завтра нам скажут, что везде нужно в интерфейсных полях показывать название вместо кода? Видите, сколько сразу возникает вопросов. И не зная ответы на все эти (и не только эти) вопросы мы не можем сказать, насколько правильно в этом случае делать «код» первичным ключом. А если мы используем id для связи — то получаем гибкое решение. Можем без проблем в интерфейсах показывать хоть код, хоть имя, хоть и то и то сразу. Причем можем делать это по-разному для разных пользователей.

                                                                                                                                                  вы видете какой-то непонятный ID и даже не понимаете схему и какие поля уникальные
                                                                                                                                                  Я сразу вижу, что ID — уникальное и это первичный ключ. Вот просто по названию, не глядя на остальные параметры полей (а также вижу, что something_id — это ключ для связи с something по id, также вот просто по названию). Но разве уникальными могут быть только поля, которые входят в первичный ключ? Или ответ на этот вопрос разве как-то зависит от того, естественный первичный ключ или суррогатный? :)

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

                                                                                                                                                    Секунду. То есть, гипотетическая ситуация в том, что у нас был естественный ключ, но внезапно сменилось требование, и теперь нужно делать join и показывать значение какого-нибудь другого поля. И вот чтобы эта ситуация не возникла, и нам не пришлось добавлять join потом, мы его добавим сразу? Ну базе данных ведь нечем больше заняться, кроме как join этот делать? А если требование не поменяется до тепловой смерти Вселенной, то кто выигрывает от менее эффективных запросов к базе? Энтропия?

                                                                                                                                                      0
                                                                                                                                                      Секунду. То есть, гипотетическая ситуация в том, что у нас был естественный ключ, но внезапно сменилось требование
                                                                                                                                                      Ключ был, и он, возможно, даже остался. Просто изменились требования к интерфейсу. Решили, что пользователям неудобно видеть код (или неудобно видеть один только код), или решили проверить вариант с отображением имени, чтобы понять, какой вариант пользователям будет удобнее (увеличит конверсии, etc.). Это не какая-то гипотетическая ситуация, это то, что в моей практике случается постоянно.

                                                                                                                                                      И вот чтобы эта ситуация не возникла, и нам не пришлось добавлять join потом, мы его добавим сразу?
                                                                                                                                                      Почему обязательно джойн? Зачастую просто пара селектов вместо одного.

                                                                                                                                                      А если требование не поменяется до тепловой смерти Вселенной, то кто выигрывает от менее эффективных запросов к базе?
                                                                                                                                                      А нам точно будет важна эффективность этих запросов к базе, чтобы на старте рисковать потенциальными потерями рабочего вермени из-за того, что выбрали изначально менее гибкий вариант и прибили все гвоздями по первоначальным требованиям?
                                                                                                                                                        0
                                                                                                                                                        выбрали изначально менее гибкий вариант

                                                                                                                                                        Самый гибкий код — это ненаписанный. Написание кода — это неизбежно ограничение пространства возможностей. Ну, и прочий дзэн. В случае же, что мы тут обсуждаем, менять запрос либо придётся всё равно, либо не придётся совсем, но "менее гибкий" вариант стоит дешевле в разработке (незначительно), и, вероятно, стоит меньше денег на работающем сервере — за счёт более высокой производительности, ведь железо очень любит код без ветвлений и циклов, а демон-оптимизатор любит много constraint'ов. Значит, больше одновременных запросов на той же мощности сервере. Конечно, ценой повышения рисков если всё-таки придётся переделать (серебряная пуля — миф). Но я за KISS+YAGNI по большинству таких вопросов (за исключением сбора требований, когда и должна определиться вероятность возникновения вами описанных изменений).

                                                                                                                                                          +2
                                                                                                                                                          «менее гибкий» вариант стоит дешевле в разработке (незначительно)
                                                                                                                                                          Даже не то чтоб незначительно, а по факту не всегда дешевле. Некоторые фреймоврки вам могут генерировать модели по умолчанию с суррогатными id (связями на основе этих id и т.п.), и менее гибкий вариант с естественным пк окажется чуть дороже в разработке (потому что все это придется определять вручную).

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

                                                                                                                                                          Но я за KISS
                                                                                                                                                          И я за. Только на мой взгляд использовние id как решения по умолчанию для пк — это несколько более S, нежели чем скурпулезный выбор для каждой сущности естественного пк, со взвешиванием всех за и против, и оцениванием, насколько высока вероятность возможных изменений в будущем. Цена ошибки в последнем случае при неверном решении может быть высока. А вот вот будет ли значительный выигрыш при верном?..
                                                                                                                                                          –2
                                                                                                                                                          Просто изменились требования к интерфейсу. Решили, что пользователям неудобно видеть код (или неудобно видеть один только код), или решили проверить вариант с отображением имени,

                                                                                                                                                          С «видеть» неинтерестно обсуждать, давайте заменим на слово «вводить».
                                                                                                                                                          Т.е. был ключ по текстовому коду, вводили его, потом решили что удобнее вводить по названию.
                                                                                                                                                          При использовании естественных ключей у вас добавляется новая операция(поиск кода по названию) и сразу загорится красный флаг, название неуникально, как я по нему буду вводить, у меня же ссылка по коду. Пойдете это обсуждать с тем кто предложил.
                                                                                                                                                          При использовании ссылки по ID велик риск этого не заменить, этот ID вы и так ищете по коду, просто замените условие поиска.
                                                                                                                                                            +1
                                                                                                                                                            С «видеть» неинтерестно обсуждать
                                                                                                                                                            Т.е. неудобный для вашего подхода пример обсуждать неинтересно… ну что, ок :)

                                                                                                                                                            При использовании ссылки по ID велик риск этого не заменить
                                                                                                                                                            А мне неинтересно обсуждать разработчиков-раздолбаев, вы уж извините. Нормальный разработчик еще до начала реализации скажет «у нас имена не уникальны, что будем с этим делать?»
                                                                                                                                                        0
                                                                                                                                                        А если мы используем id для связи — то получаем гибкое решение. Можем без проблем в интерфейсах показывать хоть код, хоть имя, хоть и то и то сразу.

                                                                                                                                                        Как раз этот случай и описывает википедия в недостатках :).
                                                                                                                                                        Склоняет администратора пропустить нормализацию. Добавить суррогатные ключи проще, чем правильно, с учётом дублирования и соотношений «1:∞» разбить БД на таблицы и проставить уникальные индексы.

                                                                                                                                                        Т.е. вместо того чтобы задавать кучу совершенно валидных вопросов, просто добавляют ID и забивают на все остальное. Потом у пользователей в лукапах появляются дублирующиеся группы или кто-то относит клиента на один код группы видимый в интерфейсе, а в базе это 2 разные записи.
                                                                                                                                                        Какие атрибуты уникальные — нужно решать

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

                                                                                                                                                        И это поддерживаю. Т.е. если вы первичный ключ делаете суррогатным ключем(который по определению не имеет совершенно никакого смысла), то вероятность ошибки будет больше
                                                                                                                                                          0
                                                                                                                                                          описывает википедия в недостатках
                                                                                                                                                          Да что вы так к Википедии прицепились? Складывается такое ощущение, что для вас это единственный источник знаний или, как минимум, истина в последней инстанции…

                                                                                                                                                          Тем более, что процитированное вами никак не отностится к тому, что я писал. Не мешает нам тут id "правильно разбить БД на таблицы и проставить уникальные индексы". Вот вообще не мешает.

                                                                                                                                                          просто добавляют ID и забивают на все остальное
                                                                                                                                                          Вам уже много раз разные люди повторили: тут проблема не в том, что id добавили, а в том, что забили на остальное.

                                                                                                                                                          суррогатным ключем(который по определению не имеет совершенно никакого смысла)
                                                                                                                                                          Вы это сами придумали? Смысл такого ключа в том, чтобы был уникальный идентификатор записи (при этом простой и неизменный).
                                                                                                                                                      0
                                                                                                                                                      Кстати, а можно пример реального случая, когда вот прямо было нужно сделать и суррогатный ключ и естественный, но его не сделали?

                                                                                                                                                      Таблица документов типа заказов. Естественный ключ — номер документа (автогенерация), решили сделать суррогатный uuid по разным причинам в том числе для предотвращения перебора по номеру. Всё было хорошо пару лет, но однажды появился дубль и заметили не сразу, в конце квартала.

                                                                                                                                                        0
                                                                                                                                                        Насколько я понял ваш пример — в нем было бы достаточно элементарной валидации на уникальность на уровне orm?
                                                                                                                                                          0

                                                                                                                                                          Побойтесь Макаронного монстра. Какая такая валидация на уникальность в подсистеме-трансляторе, которая знает только о текущей транзакции (или вообще только о текущей коллекции сущностей)? Вы имели в виду unique constraint на уровне субд.

                                                                                                                                                            0
                                                                                                                                                            Я имел ввиду валидацию в приложении для атрибута объекта. Но да, аналогичную вещь можно реализовать и через unique constraint.
                                                                                                                                                              0

                                                                                                                                                              Так это ведь не просто какая-то валидация атрибута, а проверка на уникальность? Единственный способ доказать уникальность — это посмотреть на все остальные значения (можно через хитрую структуру, типа trie) и увидеть, что значение действительно не повторяется. А это, в свою очередь, делается в приложении только через дополнительный запрос в базу. А зачем делать запрос и проверять в приложении, если по unique constraint оно и так само взорвётся при попытке записи, ещё и с достаточно однозначным кодом ошибки? Не, ну само собой запрос по проверке на вхождение в базу позволяет сэкономить на insert'e если была ошибка. Но вот если ошибки нет (а их таки как правило нет) — то экономнее будет идти через constraint.

                                                                                                                                                                0
                                                                                                                                                                Разумеется, будет запрос, но запрос элементарный, так что тут опять же тот же самый вопрос относительно того где хранить определения корректности (полагаю, уникальностью требования к номеру документа не ограничиваются).
                                                                                                                                                                  +1
                                                                                                                                                                  А зачем делать запрос и проверять в приложении, если по unique constraint оно и так само взорвётся при попытке записи, ещё и с достаточно однозначным кодом ошибки?

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

                                                                                                                                                                    0

                                                                                                                                                                    Я вот сторонник и предварительной валидации, и исключений. Валидация для пользователя, а исключение когда он умудрился пройти через валидацию с неуникальным значением.


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

                                                                                                                                                                      0

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

                                                                                                                                                                        0
                                                                                                                                                                        Ну ё-моё, вы увидели в моём сообщении "будет однозначный код ошибки" и решили, что я предлагаю показать пользователю сырой стектрейс?

                                                                                                                                                                        Нет, но я про это второе предложение написал, про ловлю исключений.

                                                                                                                                                                          0

                                                                                                                                                                          Нет ничего плохого в ловле исключений. В случае с большинством СУБД исключение будет содержать код ошибки и описание, в формате, который легко позволит это исключение показать в красивом виде конечному пользователю. Транслировали сырое исключение из базы и выбросили своё, с правильным сообщением, локализованное и вообще.

                                                                                                                                                                            0

                                                                                                                                                                            А зачем? Почему нельзя обычные проверки использовать? Ну вот в том же коде, который проверяет, что строка в поле не пустая и прочее.

                                                                                                                                                                              0

                                                                                                                                                                              Потому, что обычная проверка в коде, во-первых, даёт нулевую гарантию фактической целостности данных (полагается только на соблюдение соглашений по вызову этого кода), а во-вторых, не даёт информации демонам СУБД. Которым такая информация тоже бывает полезна для внутреннего пользования.

                                                                                                                                                                    +1
                                                                                                                                                                    Валидацию на уникальность и т.п. без unique constraint можно сделать только если уровень изоляции транзакций serialized (при этом обычно всё очень плохо с производительностью — база просто встает колом), иначе конкурирующая транзакция может вставить дубликат параллельно с вашей и предварительная проверка не спасет.
                                                                                                                                                                  0

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

                                                                                                                                                                  0
                                                                                                                                                                  Как появился дубль? Генератор UUID самописный что ли?
                                                                                                                                                                    0

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

                                                                                                                                                                  0
                                                                                                                                                                  И про преждевременность выбора более гибкого и дающего больше возможностей варианта как-то немного странно слышать от человека, который парой комментариев ниже писал про эджайл.

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

                                                                                                                                                                  Преждевременная оптимизация подразумевает, что мы создаем сложности сейчас, хотя это можно отложить на потом. В случае с первичными ключами сложность переделки потом возрастает практически до невозможности. Накопились у вас миллионы строк данных, джойны по строкам стали тормозить, появилось много пользователей, решили интернационализацию добавить, а не получится. Либо остановить сервис на N времени, поменять всю базу, потерять много денег и много пользователей. И зачем, чтобы в 2 случаях из 20 джойн не писать, когда пользователей 10 человек?


                                                                                                                                                                  Если добирается до прода и пользователи по ошибке вбивают дублированные данные исправлять это долго и сложно

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

                                                                                                                                                                    +1
                                                                                                                                                                    Да, только исправлять это надо не первичным ключом. Это валидация данных, проверка на соответствие бизнес-требованиям, уникальность это просто одно из возможных бизнес-требований.
                                                                                                                                                                    Не соглашусь. По практике работа с таблицами содержащие бессмысленные ID сложнее чем работа с таблицами где определены нормальные естественные ключи, также повышается вероятность ошибки и трудоемность(т.е. вы вынужнены выносить определение корректности БД в бизнес-логику) вместо того чтобы описать это на уровне схемы данных.
                                                                                                                                                                    Ну т.е. вот ниже обсуждали пример — таблица покупатели магазина, как идентифицировать. Разработчик которому сказали что надо использовать суррогатный ключ просто его добавит и забьет на идентификацию. Если вы объявите использование суррогатный ключей нехорошей практикой, как минимум возникнет вопрос еще на этапе создания схемы БД — а как же мы будем идентифицировать наших покупателей, и это позволит избежать многих ошибок в будущем
                                                                                                                                                                      0
                                                                                                                                                                      По практике работа с таблицами содержащие бессмысленные ID сложнее чем работа с таблицами где определены нормальные естественные ключи

                                                                                                                                                                      Вы так и не привели примера, где это так. Работа программиста с таблицами сводится к написанию запросов, в запросе у нас все равно просто названия полей, какая тут разница, что лежит в этом поле? Или вы хотите сказать, что работаете руками с реальными данными в продакшн базе?


                                                                                                                                                                      также повышается вероятность ошибки и трудоемность(т.е. вы вынужнены выносить определение корректности БД в бизнес-логику)

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


                                                                                                                                                                      Ну т.е. вот ниже обсуждали пример — таблица покупатели магазина, как идентифицировать.

                                                                                                                                                                      Нет, там обсуждали пример, как идентифицировать физических лиц. А вы от прямого ответа уклонились.


                                                                                                                                                                      Разработчик которому сказали что надо использовать суррогатный ключ просто его добавит и забьет на идентификацию.

                                                                                                                                                                      Не вижу никаких причин для такого вывода. Если есть суррогатный ключ, идентификация будет по нему. А если вообще никакой идентификации не будет, то программа работать не будет. Квалификация программиста и его желание сделать задачу никак не связаны с формой первичных ключей.


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

                                                                                                                                                                      Снова какие-то общие слова. Каких именно ошибок? Вам уже несколько человек привели конкретные примеры, когда естественный ключ гарантированно приводит к ошибкам в будущем.

                                                                                                                                                                        0
                                                                                                                                                                        У вас должно быть определение корректности в бизнес-логике, как минимум для того, чтобы показать пользователю нормальное сообщение об ошибке, неправильные поля подсветить. Бизнес-логика это вообще основное, для чего пишется код.
                                                                                                                                                                        Тоже не понимаю, почему некоторые разработчики любят размазывать логику (а определение корректности данных по сути диктуется именно бизнес-логикой) между приложением и бд в тех ситуациях, в которых на это по сути нет объективных причин (порой еще и с ее дублированием). Ведь гораздо удобнее, когда вся логика — в приложении, чем когда часть — в нем, часть — в базе, и перескакивай туда-сюда чтобы со всем этим разобраться…
                                                                                                                                                                          0

                                                                                                                                                                          Никто не любит размазывать определения корректности по множеству слоёв. Но с constraint'ами ваша база будет быстрее отвечать на некоторые запросы. Потому лучше всего всё-таки иметь определение корректности где-то в общем формате, а оттуда "переводить" определение корректности данных и на язык приложения, и на язык базы. И там же держать ключ сообщения об ошибке.

                                                                                                                                                                            0
                                                                                                                                                                            Возможно. Зависит от того, насколько велики временные затраты на использование такого (несколько более сложного, чем просто определения корректности в приложении) решения разработчиком и насколько значительно изменение производительности базы данных.
                                                                                                                                                                            0

                                                                                                                                                                            В базе последний слой защиты от некорректных данных. А так их минимум 4: валидация UI, валидация API, проверка корректности на уровне доменнной модели и на уровне БД.

                                                                                                                                                                            0
                                                                                                                                                                            Нет, там обсуждали пример, как идентифицировать физических лиц. А вы от прямого ответа уклонились.

                                                                                                                                                                            Если начали обсуждать, это по сути означает что преодолели недостаток суррогатного ключа, проблем нет. Проблемы появляются когда это не начинают обсуждать, а просто добавляют ничего не значащий ID
                                                                                                                                                                            Склоняет администратора пропустить нормализацию. Добавить суррогатные ключи проще, чем правильно, с учётом дублирования и соотношений «1:∞» разбить БД на таблицы и проставить уникальные индексы
                                                                                                                                                                              0
                                                                                                                                                                              Если начали обсуждать, это по сути означает что преодолели недостаток суррогатного ключа, проблем нет.

                                                                                                                                                                              И при суррогатном ключе тоже никаких проблем нет. Потому что и при суррогатном ключе структура базы тоже обсуждается. Невозможно сделать работающую базу, никак это не планируя, само по себе ничего не появляется.
                                                                                                                                                                              Так вы ответьте на заданный там вопрос, раз привели этот пример. Какой естественный ключ должен быть для физических лиц?


                                                                                                                                                                              Склоняет администратора пропустить нормализацию.

                                                                                                                                                                              В Википедии написана ерунда. Это утверждение бессмысленно. Если у нас есть таблица с каким-то первичным ключом, на который есть ссылки в других таблицах, значит уже по определению есть нормализация. Нормализация это устранение логической избыточности.


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


                                                                                                                                                                              проще, чем правильно, с учётом дублирования и соотношений «1:∞» разбить БД на таблицы и проставить уникальные индексы

                                                                                                                                                                              Если вы не разобьете БД на таблицы, значит у вас будет одна таблица, а значит у вас программа работать не будет. Или будет, но вам будет это очень сложно сделать. Например, вместо UPDATE строки в одной таблице по ключу надо будет делать UPDATE всех строк, где есть нужные значения. Где же тут "проще"?

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

                                                                                                                                                                                В идеальном мире наверное. А в реале