Використання таблиці конфігурації єдиного рядка в базі даних SQL Server. Погана ідея?


145

Розробляючи додаток для кошика, я виявив, що мені потрібно зберігати налаштування та конфігурації, виходячи з уподобань та вимог адміністратора. Ця інформація може бути будь-якою - від інформації про компанію, ідентифікаторів рахунку доставки, ключів API PayPal, налаштувань сповіщень тощо.

Вкрай недоцільним є створення таблиці для зберігання одного рядка в реляційній системі баз даних.

Який належний спосіб зберігати цю інформацію?

Примітка: моя СУБД - це SQL Server 2008, а рівень програмування реалізований за допомогою ASP.NET (в C #).

Відповіді:


189

У минулому я це робив двома способами - таблиця з одним рядком і таблиця з парами ключ / значення - і в кожному підході є позитиви та негативи.

Один рядок

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

Пара клавіш / вартості

  • позитивний: додавання нових налаштувань не потребує зміни схеми
  • Позитивно: схема таблиці вузька, додаткові рядки використовуються для нових налаштувань
  • мінус: кожне налаштування має однакове значення за замовчуванням (null / empty?)
  • мінус: все має зберігатися як рядки (тобто nvarchar)
  • мінус: коли ви маєте справу з налаштуваннями в коді, ви повинні знати, що таке тип налаштування, і передати його

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

Одне, що я мав на меті використовувати цей підхід, - це мати кілька рядків у "спеціальній" таблиці параметрів одного рядка. Я подолав це за допомогою (у SQL Server):

  • додавання нового бітового стовпчика зі значенням за замовчуванням 0
  • створення обмеження для перевірки, щоб переконатися, що цей стовпець має значення 0
  • створення унікального обмеження на бітовий стовпчик

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


5
Ми робимо однорядну річ у нашій програмі LOB. Усі значення мають правильний тип, що робить їх використання в додатку значно простішим. Наша схема розроблена разом із додатком, тому зміна в налаштуваннях конфігурації керується так само, як і будь-яка редакція програми.
DaveE

17
Позиція в одному рядку: може бути визначено FK в деяких стовпцях!
wqw

8
Ви завжди можете зробити пару ключів / значень з ідентифікатором типу, щоб визначити, який стовпець має значення у своєму значенні типу. Це дає вам найкраще з обох світів, і ви можете використовувати збережений прок, щоб отримати значення, коли вам це потрібно.
Middletone

19
Одне, що може насправді зіпсувати ваш день після впровадження однорядного рішення, - це, коли пізніше вам поставлено завдання "давайте також слідкуватимемо за останнім часом кожного значення, яке змінилося, і хто його змінив ...."
Дейв Матір

6
Ще одна перевага однорядного рішення, яке я виявив в одному випадку: у мене був додаток, побудований для одного клієнта, з однорядковою таблицею для "налаштувань". Пізніше я отримав двох інших клієнтів, які хотіли використовувати один і той же додаток, але хотіли різних налаштувань: все, що мені потрібно було зробити, - додати ПК "client_id" ПК до таблиці, щоб підтримувати окремий набір налаштувань для кожного клієнта. (Це коли ви усвідомлюєте, що ці "налаштування" справді є лише атрибутами для сутності вищого рівня, яку ви ще не моделювали.)
Джеффрі Кемп,

10

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


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

4
Чому проблема створення нових стовпців? Я знаю, що існують ситуації, коли розробники повинні уникати цього через політичні проблеми з оновленням SQL-схем, але про це не йдеться.
finnw

6

Один ряд буде добре працювати; він навіть матиме сильні типи:

show_borders    bit
admin_name      varchar(50)
max_users       int

Одним з недоліків є те, що alter tableдля додавання нового параметра потрібна зміна схеми ( ). Однією з альтернатив є нормалізація, коли ви закінчуєте таблицю типу:

pref_name       varchar(50) primary key
pref_value      varchar(50) 

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


4

Особисто я б зберігав його в одному ряду, якщо саме це працює. Перевищення, щоб зберігати його в таблиці SQL? певно, але справжньої шкоди в цьому немає.


4

Як ви здогадалися, і за винятком найпростіших ситуацій, розміщення всіх параметрів конфігурації в один рядок має багато недоліків. Це погана ідея ...

Зручний спосіб зберігання інформації про конфігурацію та / або тип налаштування користувача є у XML . Багато СУБД підтримують тип даних XML. Синтаксис XML дозволяє витрачати "мову" та структуру, що описує конфігурацію в міру розвитку цієї конфігурації. Однією з переваг XML є його неявна підтримка ієрархічної структури, що дозволяє, наприклад, зберігати невеликі списки параметрів конфігурації без необхідності називати їх нумерованим суфіксом. Можливим недоліком формату XML є те, що пошук та загальна зміна цих даних не є настільки прямим, як інші підходи (нічого складного, але не настільки просто / природного)

Якщо ви хочете залишитися ближче до реляційної моделі , модель Entity-Attribute-Value - це, мабуть, те, що вам потрібно, при цьому окремі значення зберігаються в таблиці, яка зазвичай виглядає так:

EntityId     (foreign key to the "owner" of this attribute)
AttributeId  (foreign key to the "metadata" table where the attribute is defined)
StringValue  (it is often convenient to have different columns of different types
IntValue      allowing to store the various attributes in a format that befits 
              them)

Завдяки чому AttributeId - це зовнішній ключ до таблиці, де визначається кожен можливий атрибут ("параметр конфігурації" у вашому випадку),

AttributeId  (Primary Key)
Name
AttributeType     (some code  S = string, I = Int etc.)
Required          (some boolean indicating that this is required)
Some_other_fields   (for example to define in which order these attributes get displayed etc...)

Нарешті, EntityId дозволяє ідентифікувати якусь сутність, яка "володіє" цими різними атрибутами. У вашому випадку це може бути UserId або навіть просто неявний, якщо у вас є лише одна конфігурація для управління.

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


3
Це здається зайвим для більшості застосувань таблиці конфігурації.
ДжерріОЛ

Я думаю, що загальна ідея цього підходу чудова. Але чому XML? Просто виберіть простий формат обміну даними, наприклад, JSON або YAML, і ви зможете отримати переваги в обох інших варіантах.
шламар

1
EAV є реляційним, але він не нормалізується. Звичайно, для цього є випадки використання (наприклад, системи ORM, схоже, люблять їх), але аргумент того, що метадані є в базі даних для EAV, не є переконливою причиною їх використання. Всі RDBMS містять метадані в системних таблицях так чи інакше, які ви можете виявити, тому таблиці однорядних рядків також мають метадані в базі даних. Жорстко кодовані назви стовпців також не є проблемою. Якщо ви використовуєте ключі для сутностей та атрибутів, то у вас є десь інша таблиця пошуку, яка визначає їх (або ще гірше, що це у вашому шарі презентації).
Давос

3

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

Додавання «таблиці змін» до вашого розгортання не здається великою компромісом для простоти та безпеки безпеки підходу до одного ряду.


2

Пара ключів і значень схожа на .Net App.Config, яка може зберігати налаштування конфігурації.

Отже, коли ви хочете отримати значення, яке ви могли б зробити:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';

1

Поширений спосіб зробити це - мати таблицю "властивостей", аналогічну файлу властивостей. Тут ви можете зберігати всі свої константи програми чи не такі постійні речі, які вам просто потрібно мати навколо.

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

property_entry_table

[id, scope, refId, propertyName, propertyValue, propertyType] 
1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN"  
2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN"  
3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN"   
4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN"  
5, 0, 1, "NOTIF_PREF", "ON", "ADMIN"  
6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN"   

Таким чином ви можете зберігати наявні вами дані, а також дані, які ви матимете в наступному році, і про них ще не знаєте :).

У цьому прикладі ваш обсяг та refId можна використовувати для того, що ви хочете на задній частині. Тож якщо у свойства typeType "ADMIN" є область 0 refId 2, ви знаєте, що це перевагу.

Тип власності стає в нагоді, коли колись вам також потрібно зберегти інформацію, яка не є адміністратором.

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

Наприклад: Якщо ви хочете зберігати свій DATABASE_VERSION , ви використовуєте таку таблицю. Таким чином, коли вам потрібно оновити додаток, ви можете перевірити таблицю властивостей, щоб побачити, яку версію свого програмного забезпечення має клієнт.

Справа в тому, що ви не хочете використовувати це для речей, що стосуються кошика. Дотримуйтесь ділової логіки у чітко визначених реляційних таблицях. Таблиця властивостей призначена лише для інформації про систему.


@finnw Я повністю погоджуюся, що цей метод не слід використовувати для пошуку, особливо коли існує багато різних типів пошуку. Можливо, я неправильно зрозумів питання. Здавалося, йому потрібна таблиця констант і властивостей системи. У такому випадку, навіщо 10 різних таблиць?
Стефано

зауважте: він сказав "зберегти налаштування та конфігурації", а не "мені потрібно зберегти реляційні дані кошика"
Стефано

Моє заперечення проти цього полягає в тому, що ви обминаєте введення тексту SQL та інші механізми обмежень, щоб уникнути оновлення схеми SQL при додаванні нових атрибутів. Як ви говорите, "дані, які ви матимете в наступному році і про них ще не знаєте". Так, у наступного року у вас з’являться нові дані, але що зупинить вас на створенні нових (набраних) стовпців SQL, ПЕРЕВІРИТИСЯ та, можливо, ЗАМОВЛЕННЯ КЛЮЧОВИХ обмежень для них під час додавання?
finnw

Перший мій інстинкт - просто додати ці дані до плоского файлу. І ви правильно, цей процес використання таблиці замість цього дійсно обійде механізми обмеження СУБД. Однак я б сказав, що якщо ви занадто сильно намагаєтесь дотримуватися належних методик баз даних, ви пропускаєте суть. Перевірте першу відповідь; найвищий голос за ТА: stackoverflow.com/questions/406760/…
Стефано

2
Я б перейшов пару ключових значень, скидаю все це у словник при запуску та сортував.
Пол Кріссі

0

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

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


0

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

Так ваш стіл виглядав би приблизно так:

id, column_num, property_name, intValue, floatValue, charValue, dateValue
1, 1, weeks, 51, , ,
2, 2, pi, , 3.14159, , 
3, 4, FiscYearEnd, , , , 1/31/2015
4, 3, CompanyName, , , ACME, 

Тут використовується трохи більше місця, але в більшості випадків ви використовуєте кілька десятків атрибутів. Для витягування / приєднання потрібного поля ви можете використовувати виписку з значення стовпчика_num.


0

Вибачте, я прийшов так, через рік. Але в будь-якому випадку те, що я роблю, є простим і ефективним. Я просто створю таблицю з трьома () стовпцями:

ID - int (11)

ім'я - варчар (64)

значення - текст

Що я роблю перед створенням нового стовпчика конфігурації, оновлення чи читання, це серіалізація "значення"! Таким чином я впевнений у своєму роді (ну, php це :))

Наприклад:

b: 0; є для B OOLEAN ( помилково )

б: 1; є для B OOLEAN ( правда )

i: 1988; є для I NT

s: 5: "Кадер"; призначений для S TRING довжиною 5 символів

Сподіваюся, це допомагає :)


1
Чому б просто не створити новий стовпець для типу? i:1988здається, ви намагаєтеся зібрати два фрагменти інформації в одну колонку.
maksymiuk

@maksymiuk SImply тому, що після несеріалізації ви отримуєте точний тип замість того, щоб використовувати цикл після (якщо або переключитись) ... тощо
Kader Bouyakoub

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

Ви маєте на увазі, роблячи щось на зразок echo (int) $varдля цілого чи іншого для інших типів?
Кадер Буякуб

0

Майте стовпчик ключа як varchar та стовпчик значень як JSON. 1є числовим, тоді як "1"є рядком. trueі falseобидва булі. Ви також можете мати об’єкти.

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