Использование таблицы конфигурации с одной строкой в ​​базе данных SQL Server. Плохая идея?


145

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

Кажется крайне неуместным создавать таблицу для хранения одной строки в системе реляционной базы данных.

Как правильно хранить эту информацию?

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

Ответы:


189

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

Один ряд

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

Пара ключ / значение

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

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

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

  • добавление нового битового столбца со значением по умолчанию 0
  • создание проверочного ограничения, чтобы гарантировать, что этот столбец имеет значение 0
  • создание уникального ограничения на битовый столбец

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


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

17
Однорядный положительный: в некоторых столбцах может быть определен FK!
wqw

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

19
Одна вещь, которая может действительно испортить ваш день после внедрения однострочного решения, - это когда вам позже будет поручено «давайте также отследим, когда в последний раз каждое значение было изменено и кто его изменил…»
Дейв Матеер,

6
Еще одно преимущество однострочного решения, которое я обнаружил в одном случае: у меня было приложение, созданное для одного клиента, с однорядной таблицей для «настроек». Позже я получил двух других клиентов, которые хотели использовать одно и то же приложение, но хотели разные настройки: все, что мне нужно было сделать, это добавить PK «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) 

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


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
Это звучит как излишнее для большинства случаев использования таблицы конфигурации.
JerryOL

Я думаю, что общая идея этого подхода великолепна. Но почему XML? Просто выберите простой формат обмена данными, такой как JSON или YAML, и вы сможете получить преимущества от обоих других вариантов.
schlamar

1
EAV является реляционным, но не нормализованным. Конечно, есть варианты использования (например, системы ORM их любят), но аргумент, что метаданные в базе данных для EAV, не является убедительной причиной для их использования. Все РСУБД в любом случае содержат метаданные в системных таблицах, которые вы можете обнаружить, поэтому однострочные таблицы также содержат метаданные в базе данных. Жестко закодированные имена столбцов также не являются проблемой. Если вы используете ключи для сущностей и атрибутов, то у вас есть жестко запрограммированная таблица поиска в другом месте, которая определяет их (или, что еще хуже, находится на уровне представления).
Давос

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 могут быть использованы для всего, что вы хотите на серверной части. Так что если propertyType "ADMIN" имеет область 0 refId 2, вы знаете, что это за предпочтение.

Тип свойства приходит на помощь, когда, когда-нибудь, вам также понадобится хранить информацию без прав администратора.

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

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

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


@finnw Я полностью согласен, что этот метод не должен использоваться для поиска, особенно когда есть много различных типов поиска. Возможно, я неправильно понял вопрос. Похоже, ему нужна таблица для констант и системных свойств. В таком случае, почему есть 10 разных таблиц?
Стефано

примечание: он сказал «сохранить настройки и конфигурации», а не «мне нужно сохранить данные реляционной корзины»
Stephano

Я возражаю против этого в том, что вы обходите механизмы ввода и другие ограничения SQL, чтобы избежать обновления схемы SQL при добавлении новых атрибутов. Как вы говорите, «данные, которые вы будете иметь в следующем году и еще не знаете». Да, у вас будут новые данные в следующем году, но что мешает вам создавать новые (типизированные) столбцы SQL, ограничения CHECK и, возможно, FOREIGN KEY для них во время их добавления?
finnw

Мой первый инстинкт - просто добавить эти данные в плоский файл. И вы правы, этот процесс использования таблицы действительно обойдет механизмы ограничения СУБД. Тем не менее, я бы сказал, что если вы слишком стараетесь следовать правильным методам работы с базами данных, вы упускаете суть. Проверьте первый ответ; проголосовали за SO: stackoverflow.com/questions/406760/…
Stephano

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, 

Он использует немного больше места, но в большинстве случаев вы используете несколько десятков атрибутов. Вы можете использовать оператор case из значения column_num, чтобы вытащить / присоединить правильное поле.


0

Извините, я пришел, да, позже. Но в любом случае, то, что я делаю, просто и эффективно. Я просто создаю таблицу с тремя () столбцами:

ID - int (11)

имя - варчар (64)

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

То, что я делаю перед созданием нового столбца конфигурации, его обновлением или чтением, состоит в сериализации «значения»! Таким образом, я уверен, что типа (ну, php есть :))

Например:

б: 0; для B OOLEAN ( ложь )

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

я: 1988; для меня NT

S: 5: "Кадер"; для S TRING длиной 5 символов

Надеюсь, это поможет :)


1
Почему бы просто не создать новый столбец для типа? i:1988Похоже, вы пытаетесь свести две части информации в один столбец.
Максимюк

@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.