Убедите скептика! Зачем мне нужна таблица ролей в моей базе данных?

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

Мысли:

  1. Базе данных не нужно знать о ролях, это область пользовательского интерфейса.
  2. Чем быстрее доступ к ролям конкретного пользователя, тем лучше
  3. Конечно, если когда-нибудь в будущем я захочу получить доступ ко всем пользователям для определенной роли, запрос может

быть немного медленным, но кого это волнует в этот момент?

Есть ли в этом смысл? Я сошел с ума? Не будет ли создание таблиц Roles и UserRole излишним и добавит ненужные накладные расходы на SQL и код?

ОБНОВИТЬ:

Чтобы еще больше проиллюстрировать мою точку зрения... в коде я хочу знать, находится ли пользователь «Стив» в роли «Администратор».

Вариант 1: запросите в таблице UserRole список ролей для пользователя «Стив». Прокрутите этот список и посмотрите, соответствует ли RoleName «Администратору».

Вариант 2: разделите CSV в свойстве «Роль пользователя» и посмотрите, содержит ли результирующий список «Администратор».

ОБНОВЛЕНИЕ II:

Я согласен с тем, что мое предложение нарушает все виды мышления типа «лучшая практика», особенно в отношении дизайна БД. Однако я не вижу смысла в «лучших практиках» в такого рода сценариях. Мне нравится время от времени раскачивать лодку лучших практик ... Мне нравится кодировать так, чтобы это казалось умным, а это значит, что иногда мне нужно больше понимать, чтобы знать, когда я не умный :)


person Steve    schedule 06.06.2011    source источник
comment
Эти обновления (вариант 1 и вариант 2). . . Никто не пишет такие запросы. Если вы хотите узнать, находится ли Стив в роли администратора, SELECT RoleName from UserRole where user = 'Steve' and RoleName = 'Administrator';. Этот запрос должен вернуть одну строку, если Стив является администратором, и ни одной строки, если он им не является. Без зацикливания. Без разделения. (Вы могли бы так же легко вернуть имя пользователя, или целое число, или логическое значение. Опять же, без циклов и без разбиения.) Меня беспокоит, что это очень, очень просто - это то, что я ожидаю от неоплачиваемого стажера. правильно каждый раз. Но ты ошибся.   -  person Mike Sherrill 'Cat Recall'    schedule 07.06.2011
comment
@Catcall - Не будьте так суровы, есть старшие разработчики и корпоративные приложения, написанные таким образом. Многие люди спешат и в конечном итоге следуют этому конкретному подходу. Это очень простой дизайн, поэтому для кого-то вроде @Steve он кажется лучшим выбором и почти логичным выбором, если вы не думаете об этом в решении на основе набора. В конечном итоге это будет его решение создать эту функциональность, но мы здесь для рекомендаций / предложений. Сказать, что это просто и стажер понимает, совсем не помогает.   -  person JonH    schedule 07.06.2011
comment
@JonH: Многие вещи кажутся логичными, если о них не думать. Большинство программистов считают, что отсутствие мышления плохо масштабируется. И я не только сказал, что это элементарно и стажер «понимает» это; Сначала я предоставил одну версию правильного запроса. Если Стив собирается спроектировать и построить пригодную для использования базу данных, ему нужно правильно понять основы. В SQL вы не получите ничего более базового, чем научиться использовать предложение WHERE.   -  person Mike Sherrill 'Cat Recall'    schedule 08.06.2011
comment
@Catcall: я допустил ошибку в примере запроса sql, вы правы. Я совмещал размышления о GetAllRolesForUser с размышлениями об IsUserInRole. Хотите верьте, хотите нет, но я знаю, как написать простое предложение WHERE — я рад, что этот пост не является попыткой убедить кого-то нанять меня. Что касается того, чтобы не думать - если бы я собирался не думать, я бы пошел вперед со стандартным набором таблиц ролей, но я пытался понять, ПОЧЕМУ стоит этот стандарт. Возможно, я неправильно сформулировал вопрос ИЛИ ТАК не место для обсуждения концепций дизайна?   -  person Steve    schedule 08.06.2011
comment
а) Что касается «не мышления». . . Я отвечал Джону, а не тебе. б) Кажется, что некоторые люди голосуют за закрытие вопросов дизайна, но я не один из них. Программирование — это гораздо больше, чем написание и отладка кода. c) СУБД — это единственная часть программного обеспечения, которая может гарантировать последовательное применение ограничений целостности ко всем пользователям и всем приложениям. (См. мой предыдущий ответ ниже.)   -  person Mike Sherrill 'Cat Recall'    schedule 08.06.2011


Ответы (5)


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

Нарушение данных

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

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

User table
UserID
User
...

Role table
RoleID
Role
....

User Role Table (the relationship)
UserRoles
UserID
RoleID
...

Обновление/вставка данных

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

Поиск подходящей роли

Здесь кроется самая сложная проблема, как найти роль, данную пользователю. Вы можете придумать эту замечательную технику синтаксического анализа на C# или SQL Server, она отлично работает... но становится ужасно медленной и трудной для чтения. Вы начинаете иметь дело с SubString(), Left(), Right(), Len() и целым рядом других функций только для того, чтобы проанализировать роль пользователя.

Решение

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

person JonH    schedule 06.06.2011
comment
Почему роль должна быть сущностью в базе данных? Это потому, что управление базой данных является стандартным, а не самими бизнес-правилами? (Это означает, что база данных просто хранит данные, а приложения могут работать с данными в соответствии со своими собственными бизнес-правилами, определенными в коде.) - person Steve; 06.06.2011
comment
@Steve - Где-то вам нужно хранить роли, зачем вам жестко кодировать роли в вашем реальном приложении. В этом случае вы вынуждены распространять/перезаливать ваше приложение в зависимости от типа (клиент/веб/и т.д.). Роль может увеличиваться или уменьшаться, поэтому вы должны иметь возможность добавлять/удалять записи из уровня данных, ведь роль — это часть данных, нет? Если вы в конечном итоге поместите это в структуру приложения, вы собираетесь превратить свою жизнь в сущий ад :). - person JonH; 06.06.2011
comment
@Steve - сегодня вашей БД не нужно знать о ролях, завтра все изменится, планируйте будущее и развивайте систему с умом. Прими удар сейчас и выполняй работу правильно, прежде чем удар превратится в кошмар. - person JonH; 06.06.2011
comment
Ввод нескольких значений в одно поле, разделенное запятыми, фактически нарушает первую нормальную форму. Это повторяющаяся группа. Я согласен с остальной частью вашего ответа. - person Walter Mitty; 07.06.2011
comment
@JonH - пожалуйста, смотрите мои обновления вопроса. Получение роли для пользователя кажется мне более тривиальным, а не менее — я что-то упускаю? Я не сторонник умных данных в базе данных, позже я видел много проблем с гибкостью, поэтому я предпочитаю глупые данные с метаданными и бизнес-правилами, которые делают эти данные чего-то стоящими. - person Steve; 07.06.2011
comment
@ Стив, я сдаюсь, делай по-твоему. - person JonH; 09.06.2011

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

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

EDITED TO ADD: Вы указываете, что приложение в конечном итоге будет принимать решения на основе значений, возвращаемых базой данных; например предоставление определенных опций, если у пользователя есть роль «Администратор». Это правда, и это еще одно отдельное место, где непротиворечивость, например, имен ролей может быть нарушена. Я не думаю, что денормализация базы данных делает это менее вероятным.

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

То есть в представлении, если вы решаете, показывать ли кнопку «редактировать» в описании элемента, вы не проверяете, выполнив if role=='ADMIN' or role=='EDITOR', вы проверяете, выполнив if user.can_edit(item). Где-то еще вы установили, что администраторы и редакторы имеют возможность редактировать элементы. См., например, подход, который использует система авторизации Rails CanCan.

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

person Jacob Mattison    schedule 06.06.2011
comment
Я согласен с концепцией, но мне интересно, почему база данных должна заботиться? Мое приложение ДОЛЖНО знать о ролях, потому что оно должно представлять роли в пользовательском интерфейсе и должно обеспечивать соблюдение бизнес-правил, связанных с различными ролями. Так почему бы просто не определить роли в приложении и только в приложении? (Вместо того, чтобы приложение спрашивало БД, что такое роли, и сопоставляло роли БД с чем-то, о чем знает приложение, и т. д.) - person Steve; 06.06.2011
comment
Если вы хотите жестко закодировать роли в приложении, я понимаю вашу точку зрения. Хотя это, конечно, было бы ужасной идеей, поскольку роли часто приходится менять на лету. Но если роли — это данные (а они таковы), то я думаю, что с ними следует обращаться как с данными, включая неповторяющиеся данные, которые не нужно повторять. Это не вопрос базы данных, управляющей приложением, это просто вопрос настройки данных, чтобы вы могли обеспечить логическую согласованность. - person Jacob Mattison; 06.06.2011
comment
В какой-то момент мне придется жестко закодировать данные роли в моем приложении, верно? Мне приходится принимать решения в коде на основе данных ролей, поэтому в какой-то момент я должен знать, что 100001 — это Администратор. - person Steve; 06.06.2011
comment
Неа. В таблице ролей есть фактическое имя роли, поэтому вы используете идентификатор роли, чтобы присоединить пользователя к роли, и используете имя роли, чтобы узнать, кто является администратором. НИКОГДА НИКОГДА не ссылайтесь на идентификатор базы данных в своем коде; они, вероятно, будут отличаться от одного экземпляра базы данных к другому (например, от тестового к рабочему экземпляру приложения). - person Jacob Mattison; 06.06.2011
comment
@JacobM Да, хорошая мысль. Итак, где-то мое приложение должно знать, что роль администратора равна роли БД с именем роли администратора :) Либо у меня будут жестко запрограммированные точки принятия решений с волшебной строкой администратора, либо у меня будет перечисление какой-то вид, который я использую для принятия решений в коде - это то, что я пытаюсь сделать. - person Steve; 07.06.2011
comment
Я начал отвечать в комментарии, но мне было что сказать, поэтому я отредактировал свой ответ, чтобы ответить. - person Jacob Mattison; 07.06.2011
comment
@JacobM - спасибо за ссылку на CanCan, это своего рода умный код, который меня интересует. Однако я не понимаю, как это делает хранение ролей в базе данных полезным. Мне кажется, что методология CanCan заключает ум и силу в код, а не в БД... - person Steve; 07.06.2011
comment
Подход CanCan означает, что существует единая точка интерфейса между именами ролей в коде и именами ролей в базе данных. Независимо от того, используете ли вы нормализованный подход или нет, у вас будет вероятность несогласованности, поэтому подход CanCan сводит к минимуму эти возможные ошибки. Мое намерение состояло в том, чтобы опровергнуть вашу точку зрения о необходимости где-то жестко закодировать имена ролей в вашем коде - да, вы должны это сделать (независимо от того, нормализуете вы или нет), но вы можете минимизировать это с помощью этого подхода. В приложении на основе CanCan слово ADMIN появляется в коде ровно один раз. - person Jacob Mattison; 07.06.2011

  1. Вам нужна дополнительная логика для анализа вашего CSV на стороне кода.
  2. Большинство ORM не поддерживает CSV.
  3. Если у вас длинное имя роли (например, UserWhoHaveAccessToXObjButNotYObjAndAtTheSameTimeCanDoZ), то вы теряете место, повторяя его для всех пользователей.

Кроме того, ваше первое предположение справедливо не для всех ситуаций. Что, если МОЕЙ базе данных нужно знать о ролях?

person Thomas Li    schedule 06.06.2011
comment
Мой код все равно должен будет переводить информацию из базы данных, поэтому передача csv поставщику ролей не должна быть дополнительной работой: на самом деле это может быть меньше работы в коде. Пункт 3 является интересным соображением... Однако, если пространство дешевое, а скорость важнее, стоит ли требовать дополнительных таблиц и объединений? - person Steve; 06.06.2011
comment
@Steve - Это самое замечательное в базах данных, они реляционные, поэтому соединения - это очень и очень нормально. Я видел сообщения, в которых люди упоминали, создавать ли эти огромные таблицы, чтобы избежать объединений. Ответ: вы НЕ избегаете соединений в базе данных, это функция, которую следует использовать, потому что, в конце концов, это РЕЛЯЦИОННЫЕ данные. - person JonH; 06.06.2011

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

person Walter Mitty    schedule 06.06.2011
comment
Я склонен согласиться с @WalterMitty. Работа базы данных заключается в хранении и защите данных точно так же, как банк хранит вашу сейфовую ячейку. Банк не требует, чтобы я размещал свои предметы в ящике в каком-либо определенном порядке, что позволяет мне применять мои бизнес-правила к содержимому ящика. Если данные будут использоваться только приложением в соответствии с дизайном приложения, просто сохраните их в шаблоне, указанном в документах, строках, числах и т. д., и передайте их им так же, как они предоставили их вам для хранения. Если БД должна использовать его для бизнес-правил, НЕ сохраняйте его в строке с запятой. - person RC_Cleland; 07.06.2011

Ты сказал

Так почему бы просто не определить роли в приложении и только в приложении?

Вы можете сделать это надежно, только если честно скажете: «Я на 100% уверен, что каждый программист и каждый администратор базы данных вручную сохранит целостность данных, независимо от того, какое приложение прикасается к этой базе данных, независимо от того, на каком языке оно запрограммировано, независимо от того, насколько опытен или неопытен программист, и неважно, насколько сложной становится база данных, с этого момента и до конца ее жизни».

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

person Mike Sherrill 'Cat Recall'    schedule 06.06.2011
comment
И вероятность того, что это произойдет, очень и очень мала... - Я готовлюсь к оптимальным решениям, потому что через 10 лет на этом задании может появиться 10 новых разработчиков. Облегчить им жизнь. - person JonH; 09.06.2011
comment
Нет, сейчас сейчас у вас, вероятно, есть как минимум три приложения, которые имеют доступ к вашей базе данных. У вас есть код приложения, графический пользовательский интерфейс dbms и пользовательский интерфейс командной строки dbms. Разработчики приложений нередко считают, что они могут выполнять работу СУБД так же хорошо, как СУБД. Обычно они ошибаются. - person Mike Sherrill 'Cat Recall'; 09.06.2011