Чому ви зберігаєте перерахунок у БД?


69

Я бачив низку таких питань, як це , запитуючи поради щодо збереження перерахунків у БД. Але мені цікаво, чому б ти це зробив. Тож скажімо, що у мене є сутність Personіз genderполем та Genderперерахунком. Потім у таблиці моїх осіб є гендерна колонка.

Окрім очевидної причини встановлення правильності, я не бачу, чому я створив би додаткову таблицю genderдля відображення того, що вже є у моїй програмі. І мені не дуже подобається це дублювання.



1
Де б ви ще зберігали дані, які можуть регулярно змінюватися? Хоча ви, можливо, думали про всі варіанти, що робити, якщо хтось прийде і хоче додати новий варіант. Ви готові виправити цей жорстко кодований список? Хтось може захотіти визначити свою стать як щось інше, ніж чоловіче чи жіноче, наприклад, інтерсекс.
Король JB

4
@JBKing ... просто подивіться на гендерний список Facebook.


3
Якщо ваші клієнти є "облудними Tumblrites", то ви, чорт заздалегідь, створите схему бази даних, яка дозволяє вам створити щось, що відповідає їх потребам, принаймні, якщо ви збираєтесь залишатися в бізнесі.
Стівен Бернап

Відповіді:


74

Візьмемо ще один приклад, який менш загрожує концепціями та очікуваннями. У мене тут перерахунок, і це набір пріоритетів для помилки.

Яке значення ви зберігаєте в базі даних?

Таким чином, я міг би зберігати 'C', 'H', 'M'і 'L'в базі даних. Або 'HIGH'тощо. У цьому виникає проблема строго типізованих даних. Існує відомий набір допустимих значень, і якщо ви не зберігаєте цей набір у базі даних, це може бути важко працювати.

Чому ви зберігаєте дані в коді?

У вас є List<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'};чи щось для цього в коді. Це означає, що у вас є різні відображення цих даних у відповідному форматі (ви вставляєте всі шапки в базу даних, але ви їх відображаєте як Critical). Ваш код також важко локалізувати. Ви прив'язали представлення бази даних ідеї до рядка, який зберігається в коді.

Де б ви не мали доступу до цього списку, вам потрібно мати копію коду або клас із купою констант. Жоден з них не є хорошими варіантами. Не слід також забувати, що є й інші програми, які можуть використовувати ці дані (які можуть бути написані іншими мовами - у веб-додатку Java використовується система звітності Crystal Reports та дані, що містять пакетне завдання Perl ). Двигун звітів повинен знати дійсний перелік даних (що відбувається, якщо в 'LOW'пріоритеті нічого не позначено, і вам потрібно знати, що це дійсний пріоритет для звіту?), А пакетне завдання матиме інформацію про те, що є дійсним значення є.

Гіпотетично, ви могли б сказати : «ми єдина мова магазин - все написано в Java» і є один .jar, що містить цю інформацію , - але тепер це означає , що ваші додатки тісно пов'язані один з одним і що .jar , що містить дані. Вам потрібно буде випустити звітну частину та частину пакетного оновлення разом із веб-додатком щоразу, коли відбудеться зміна - і сподіваємось, що цей випуск пройде гладко для всіх частин.

Що відбувається, коли ваш начальник хоче іншого пріоритету?

Ваш начальник сьогодні прийшов. Новий пріоритет - CEO. Тепер вам потрібно перейти і змінити весь код і зробити перекомпіляцію та повторне розміщення.

За допомогою підходу "перерахування таблиці" ви оновлюєте список перерахунків, щоб мати новий пріоритет. Весь код, який отримує список, витягує його з бази даних.

Дані рідко стоять окремо

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

Повернувшись до гендеру, про який ми згадували у запитанні: Стать має посилання на використовувані займенники: he/his/himі she/hers/her..., і ви хочете уникнути жорсткого кодування цього коду. І тоді ваш бос приходить, і вам потрібно додати, що ви отримали 'OTHER'гендерну групу (щоб це було просто), і вам потрібно пов’язати цю стать з they/their/them... і ваш начальник бачить, що має Facebook і ... ну так.

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

Що з іншими сховищами даних?

Незалежно від того, де ви зберігаєте це, існує той самий принцип.

  • Ви можете мати файл priorities.prop, у якому є список пріоритетів. Ви читаєте цей список у файлі властивостей.
  • Ви можете мати базу даних сховища документів (наприклад, CouchDB ), в якій є запис enums(а потім написати функцію перевірки в JavaScript ):

    {
       "_id": "c18b0756c3c08d8fceb5bcddd60006f4",
       "_rev": "1-c89f76e36b740e9b899a4bffab44e1c2",
       "priorities": [ "critical", "high", "medium", "low" ],
       "severities": [ "blocker", "bad", "annoying", "cosmetic" ]
    }
    
  • У вас може бути XML-файл з трохи схемою:

    <xs:element name="priority" type="priorityType"/>
    
    <xs:simpleType name="priorityType">
      <xs:restriction base="xs:string">
        <xs:enumeration value="critical"/>
        <xs:enumeration value="high"/>
        <xs:enumeration value="medium"/>
        <xs:enumeration value="low"/>
      </xs:restriction>
    </xs:simpleType>
    

Основна ідея така ж. Сам сховище даних - це те, де список дійсних значень потрібно зберігати та застосовувати. Розмістивши його тут, простіше міркувати про код і дані. Вам не потрібно турбуватися про те, щоб захисно перевіряти, що ви маєте кожен раз (це верхній регістр? Чи нижній? Чому chriticalв цьому стовпці є тип? Тощо ...), оскільки ви знаєте, що ви повертаєтесь із сховища даних саме те, що в сховищі даних очікує, що ви надішлете інакше - і ви можете запитати у сховищі даних щодо списку дійсних значень.

Винос

Набір допустимих значень - це дані , а не код. Ви дійсно повинні прагнути до DRY коду - але проблема дублювання в тому , що ви дублювати дані в коді, а не поважаючи своє місце в якості даних і зберігання в базі даних.

Це спрощує написання декількох додатків проти сховища даних і уникає випадків, коли вам потрібно буде розгорнути все, що щільно пов'язане з самими даними - адже ви не зв'язали свій код із даними.

Це полегшує тестування програм, тому що вам не доведеться повторно перевіряти всю програму, коли CEOдодається пріоритет - тому що у вас немає коду, який би дбав про фактичне значення пріоритету.

Можливість міркувати про код і дані незалежно один від одного полегшує пошук та виправлення помилок під час обслуговування.


6
Якщо ви можете додати значення коду до свого коду без необхідності змінювати будь-яку логіку (і, щоб не бути його локалізованим відображенням), я сумніваюся в необхідності додаткового значення перерахунку в першу чергу. І хоча я досить дорослий, щоб оцінити можливість легко запитувати резервні копії баз даних за допомогою простих SQL-запитів для аналізу проблеми, з ORM в ці дні ви можете зробити дуже добре, не вимагаючи взагалі бачити базову базу даних. Я не розумію тут суті щодо локалізації (займенників) - цей матеріал, безумовно, не повинен бути в базі даних, але я б сказав, якісь файли ресурсів.
Во

1
@Voo займенники є прикладом інших даних, пов’язаних із цим enumeque значенням. Без даних у таблиці, строго набрані значення повинні бути там, без відповідних обмежень FK. Якщо ви маєте займенники (як це) у файлі ресурсу, у вас є зв'язок між базою даних та файлом (оновіть базу даних та перезавантажте файл). Розглянемо переліки червоного кольору, які можна змінювати через адміністраторський інтерфейс під час польоту без необхідності повторного використання .

1
... пам’ятайте також, що бази даних є сховищем поліглотів. Якщо ви вимагаєте зробити перевірку як частину ORM однією мовою, ви зробили необхідним дублювати цю перевірку будь-якою іншою мовою, якою ви користуєтесь (я нещодавно працював із передньою частиною Java, яка Python пересилала дані в базу даних - Java ORM та системи Python повинні погоджуватись у речах, - і ця угода (дійсні типи) була найпростішою реалізацією, коли база даних застосовувала її за допомогою таблиці "enum".).

2
@Voo Redmine використання enum - це те саме, що і bugzilla "найважливіша таблиця містить усі помилки системи. Вона складається з різних властивостей помилок, включаючи всі значення перерахунку, такі як серйозність та пріоритет". - Це не текстове поле у ​​вільній формі, це значення, яке є одним із цього відомого і переліченого набору. Це не перерахунок часу компіляції , але його все ще переживають. Дивіться також Mantis .

1
Отже, для підтвердження - Ваша думка полягає в тому, що люди ніколи не повинні використовувати Enums? Не було зрозуміло.
niico

18

Як ви вважаєте, який із них, швидше за все, помиляється під час читання запиту?

select * 
from Person 
where Gender = 1

Або

select * 
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female" 

Люди складають таблиці перерахунків у SQL, оскільки вони вважають, що останні є більш читабельними - що призводить до меншої кількості помилок при написанні та підтримці SQL.

Ви можете зробити гендерну рядок безпосередньо в Person, але тоді вам доведеться спробувати застосувати справу. Ви також можете збільшити кількість запитів на зберігання таблиці та часу запиту через різницю між рядками та цілими числами, залежно від того, наскільки чудовим є ваш БД при оптимізації речей.


5
Але потім ми приєднуємось до столів. Якщо у моєї організації є два перерахунки, я приєднаюся до трьох таблиць просто для простого запиту.
user3748908

11
@ user3748908 - так? Приєднання - це те, у чому БД хороший, а альтернативи гірші - принаймні в очах людей, які обрали цей маршрут.
Теластин

8
@ user3748908: Бази даних не тільки добре допомагають приєднуватись, вони також дуже добре застосовують послідовність. Забезпечення послідовності спрацьовує дуже добре, коли ви можете вказати стовпчик в одній таблиці на ідентифікаційний рядок іншої та сказати, що "значення для цього стовпця повинно бути одним із ідентифікаторів цієї таблиці".
Blrfl

2
Це все правда, але є багато випадків, коли вам потрібно пожертвувати приєднаннями з міркувань продуктивності. Не зрозумійте мене неправильно. Я все про цей тип дизайну та приєднання, але я кидаю, що світ не закінчиться, якщо ви знайдете, що вам часом не потрібні приєднання через продуктивність.
Джон

3
Якщо вам доведеться відмовитись від приєднання до довідкових таблиць з міркувань продуктивності @JonH, вам потрібно придбати більший сервер або перестати намагатися просувати предикати через велику кількість підзапитів (я припускаю, що ви знаєте, що ви робите). Таблиці довідників - це дані, які повинні знаходитися у вашому кеші протягом декількох секунд після запуску БД.
Бен

10

Я не можу повірити, що люди ще не згадували про це.

Іноземні ключі

Зберігаючи enum у вашій базі даних та додаючи в таблицю зовнішній ключ, який містить значення enum, ви гарантуєте, що жоден код ніколи не вводить неправильні значення для цього стовпця. Це допомагає вашій цілісності даних і є найбільш очевидною причиною того, що у IMO у вас повинні бути таблиці для перерахунків.


Питання завдовжки всього 5 рядків і чітко зазначає "Крім очевидної причини забезпечення правильності". Тож ніхто цього не згадував, тому що ОП заявляє, що це очевидно, і він шукає інших виправдань - PS: Я згоден з вами, це досить вагома причина.
користувач1007074

6

Я в таборі, який погоджується з вами. Якщо ви зберігаєте в своєму коді гендерні перерахунки та tblGender у своїй базі даних, у вас може виникнути проблеми, коли настане час технічного обслуговування. Вам потрібно буде документувати, що ці два об'єкти повинні мати однакові значення, і тому будь-які зміни, внесені до одного, ви також повинні внести до іншого.

Потім вам потрібно буде передати значення перерахувань до ваших збережених процедур так:

create stored procedure InsertPerson @name varchar, @gender int
    insert into tblPeople (name, gender)
    values (@name, @gender)

Але подумайте, як би ви це зробили, якби ви зберегли ці значення в таблиці бази даних:

create stored procedure InsertPerson @name varchar, @genderName varchar
    insert into tblPeople (name, gender)
    select @name, fkGender
    from tblGender
    where genderName = @genderName --I hope these are the same

Звичайно, реляційні бази даних побудовані з урахуванням приєднань, але який запит читати простіше?


Ось ще один приклад запиту:

create stored procedure SpGetGenderCounts
    select count(*) as count, gender
    from tblPeople
    group by gender

Порівняйте це з цим:

create stored procedure SpGetGenderCounts
    select count(*) as count, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender
    group by genderName --assuming no two genders have the same name

Ось ще один приклад запиту:

create stored procedure GetAllPeople
    select name, gender
    from tblPeople

Зауважте, що в цьому прикладі вам доведеться перетворити гендерну клітинку у своїх результатах з int в enum. Однак ці конверсії прості. Порівняйте це з цим:

create stored procedure GetAllPeople
    select name, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender

Усі ці запити є меншими та більш доступними для вирішення вашої ідеї уникнення визначень перерахунків із бази даних.


1
Що робити, якщо це не гендер. Я думаю, що ми занадто звисаємо до того, що гендер є полем. Що робити, якщо ОП сказала "Отже, скажімо, що у мене є помилка сутності з полем пріоритету" - чи змінилася б ваша відповідь?

4
@MichaelT Перелік можливих значень "пріоритету" є частиною коду принаймні в тій же мірі, в якій він є частиною даних. Ви бачите графічні значки для різних пріоритетів? Ви не очікуєте, що їх знімуть з бази даних? І такі речі можуть бути тематичними та стильовими та все ще представляти той самий діапазон значень, що зберігаються в БД. Ви не можете просто змінити його в базі даних; у вас є код презентації для синхронізації.
Євген Рябцев

1

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


1

Спершу вам потрібно визначитися, чи буде база даних коли-небудь використовуватися лише одним додатком, або є можливість для декількох додатків використовувати її. У деяких випадках база даних - це не що інше, як формат файлу для програми (у цьому плані часто можна використовувати бази даних SQLite). У цьому випадку біт, що дублює визначення enum як таблиці, часто може бути нормальним і може мати більше сенсу.

Однак, як тільки ви хочете розглянути можливість наявності декількох додатків для доступу до бази даних, тоді таблиця для перерахунку має багато сенсу (інші відповіді розглядаються чому детальніше). Інша річ, яку ви повинні врахувати, чи хочете ви чи інший розробник подивитися на необроблені дані бази даних. Якщо це так, це можна вважати іншим використанням додатків (лише те, де лабораторна мітка є необробленою SQL).

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


1

Якщо у вас є перерахування коду, який використовується для керування логікою бізнесу в коді, ви все одно повинні створити таблицю для представлення даних у БД з багатьох причин, описаних вище / нижче. Ось кілька порад, щоб переконатися, що ваші значення БД синхронізуються зі значеннями коду:

  1. Не робіть поле ідентифікатора на таблиці стовпцем особи. Додайте ідентифікатор та опис як поля.

  2. Зробіть щось інше в таблиці, що допоможе розробникам знати, що значення напівстатичні / прив’язані до перерахування коду. У всіх інших таблицях огляду (зазвичай там, де значення можуть бути додані користувачами), як правило, у мене є LastChangedDateTime та LastChangedBy, але відсутність їх у таблицях, пов’язаних із перерахуванням, допомагає мені пам’ятати, що вони змінюються лише розробниками. Задокументуйте це.

  3. Створіть код підтвердження, який перевіряє, чи є кожне значення перерахунку у відповідній таблиці та що у відповідній таблиці знаходяться лише ці значення. Якщо у вас є автоматизовані програми "тести здоров'я", які виконуються після складання, у нього є. Якщо ні, змушуйте код запускатись автоматично при запуску програми кожного разу, коли програма запущена в IDE.

  4. Створіть доставку сценаріїв SQL, які виконують те саме, але всередині БД. Якщо вони створені правильно, вони також допоможуть у міграції навколишнього середовища.


0

Залежить також від того, хто отримує доступ до даних. Якщо у вас є лише один додаток, це може бути добре. Якщо ви додасте в сховище даних або систему звітності. Вони повинні знати, що означає цей код, що таке редакційна версія коду для людини.

Зазвичай таблиця типів не дублюється як перерахунок у коді. Ви можете завантажити таблицю типів у списку, кешованому.

Class GenderList

   Public Shared Property UnfilteredList
   Public Shared Property Male = GetItem("M")
   Public Shared Property Female = GetItem("F")

End Class

Часто набирайте і йде. Вам потрібна дата, коли новий тип було додано. Знайте, коли конкретний тип було видалено. Показувати його потрібно лише за потреби. Що робити, якщо клієнт хоче "трансгендер" як стать, а інші клієнти цього не роблять? Вся ця інформація найкраще зберігається в базі даних.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.