При разработке баз данных, каков предпочтительный способ хранения нескольких значений true / false?
Как указано в заголовке, при разработке баз данных, каков предпочтительный способ обработки таблиц, которые имеют несколько столбцов, которые просто хранят значения true/false как только одно или значение (например, "Y / N: или "0/1")? Аналогично, существуют ли некоторые проблемы, которые могут возникнуть между различными базами данных (например, Oracle и SQL Server), которые могут повлиять на то, как обрабатываются столбцы?
10 ответов
на SQL Server
, есть BIT
тип данных. Вы можете хранить 0 или 1 там, сравнивать значения, но не запускать MIN
или MAX
.
на Oracle
, вы просто использовать NUMBER
или CHAR(1)
.
на MySQL
и PostgreSQL
любой тип данных неявно конвертируется в BOOLEAN
.
обе системы поддерживают BOOLEAN
тип данных, которые вы можете использовать как есть, без операторов, в WHERE
или ON
п.:
SELECT *
FROM mytable
WHERE col1
, что невозможно в SQL Server
и Oracle
(вам нужно иметь какой-то предикат или предикат).
на MySQL
, BOOLEAN
синоним TINYINT(1)
.
на PostgreSQL
тоже (с точки зрения хранения), но логически это не неявно конвертируется в любой другой тип.
по моему собственному опыту, я предпочитаю char (1) для " Y " или "N". Использование 0 и 1 может быть немного запутанным в зависимости от того, сколько пива я уже выпил, а функция c++ main() возвращает 0 при успехе. Типы ENUM и BIT больше проблем, чем они стоят.
интересно отметить, что MySQL information_schema
использует VARCHAR (3) Для " да " или "нет".
пример:
information_schema.USER_PRIVILEGES (
...
IS_GRANTABLE VARCHAR(3) NOT NULL DEFAULT ''
)
вместо логических типов данных может потребоваться рассмотреть другую модель данных для хранения логических значений, которая может быть особенно уместна в следующих случаях:
- когда у вас будет много столбцов да/нет.
- когда вам, вероятно, потребуется добавить больше столбцов да/нет в будущем.
- когда значения да/нет не меняются очень часто.
определение разрешений пользователя может быть типичным примером выше. Рассмотрим следующие таблицы:
Table "Users": (user_id, name, surname, country)
Table "Permissions": (permission_id, permission_text)
Table "Users_Permissions": (user_id, permission_id)
на Permissions
таблицы вы можете определить все возможные разрешения, которые могут быть применимы к пользователям. Вам нужно будет добавить строку в Permissions
таблица для каждого атрибута yes/no. Как вы могли заметить, это упрощает добавление новых разрешений в будущем без необходимости изменения схемы базы данных.
С вышеуказанной моделью вы затем укажете истинное значение, назначив user_id
С permission_id
in the Users_Permissions
таблица. В противном случае по умолчанию будет FALSE.
например:
Table "Permissions"
permission_id text
-----------------------------------
1 "Read Questions"
2 "Answer Questions"
3 "Edit Questions"
4 "Close Questions"
Table "Users_Permissions"
user_id permission_id
-----------------------------------
1 1
1 2
1 3
2 1
2 3
преимущества
- индексации: вы можете легко использовать индекс для запроса конкретных фактов.
- пробел: конвенции по умолчанию экономит место, когда у вас есть много ложных ценностей.
-
нормированный: факты определяются в их собственных таблицах (в
Permissions
иUsers_Permissions
таблицы.) Вы можете легко хранить больше информации о каждом факте.
недостатки
- запросы: для простых запросов потребуются соединения.
-
значение False: чтобы установить значение false, вам нужно будет удалить строку (из
Users_Permissions
таблица.) В противном случае вы можете использовать флаг "deleted" вUsers_Permissions
стол, который позволит вам хранить информацию для аудита, таких, как когда разрешение было изменено и кем. Если вы удалите строку, вы не сможете сохранить эту информацию.
используйте все, что имеет смысл для конкретного компонента database engine, который вы используете. Это интерфейс к базе данных, который должен ее обрабатывать. Если интерфейс на стороне кода к базе данных достаточно модульный, то это будет не более чем простое однострочное изменение для обработки другого логического типа в базовой базе данных.
Я думаю, что значения" Y/N "более значимы, чем"1/0". С Oracle я бы сделал следующее, чтобы данные были проверены как можно больше с помощью компонента database engine:
- определите столбцы как char (1)
- добавить проверочное ограничение, что можно значения ограничены значением " in ('Y', 'N')
- Если соответствует бизнес-правилам, сделайте их не nullable - это может избежать проблем, когда вы неявно предположим, что все, что не является "Y" имеет значение 'N' в вашем SQL
Если ваша СУБД поддерживает логический тип данных, например MySQL, используйте его. Если это не так, как Oracle, я обычно использую char(1) со значениями Y или N. В последнем случае неплохо написать пару функций для преобразования вашего Java или C++ или любого логического типа В и из Y/N, чтобы избежать избыточного кода для этого. Это довольно тривиальная функция, но ей придется иметь дело с такими случаями, как нули или значения, отличные от Y или N, и вы хотите делать это последовательно.
I определенно не будет упаковывать флаги в одну переменную с битовыми операциями. Да, это сэкономит некоторое дисковое пространство, но цена намного больше сложности и возможностей для ошибок. Если ваша СУБД не поддерживает битовые операции - а у меня никогда не было желания делать такие вещи, я не знаю с головы до ног, что, если таковые имеются, - тогда вам будет очень сложно выбрать или отсортировать на основе такого флага. Конечно, вы можете получить все записи, соответствующие другим критериям, а затем вызывающий код отсеивает те, у которых есть правильное значение флага. Но что, если только небольшой процент записей имеет желаемое значение флага, и у вас есть запрос, который соединяет многие другие записи? Например, " выберите сотрудника.имя, сумма (pay.суммы) от работника присоединиться оплатить используя (ид_сотрудника), где сотрудник.executive=true и pay.бонус=правда". С предложением where вы, вероятно, получите очень скромное количество записей. Без него вы получите всю базу данных.
дисковое пространство дешево дней, поэтому любая экономия диска, вероятно, будет неважной. Если у вас действительно есть огромное количество флагов-например, сотни или тысячи на запись-я полагаю, что может быть случай для их упаковки. Но это было бы далеко в моем списке вариантов дизайна.
Edit: позвольте мне подробно рассказать о написании класса для преобразования вашего "SQL boolean"в" Java boolean". То же самое относится к любому языку, но я буду использовать Java в качестве примера.
Если СУБД имеет встроенный логический тип, то с Java вы можете прочитать это непосредственно в логическую переменную с ResultSet.getBoolean().
но если вам нужно сохранить его, скажем, как символ "Y" или "N", тогда вы должны прочитать его в строку. Поэтому мне имеет смысл объявить такой класс:
class MyBoolean
{
boolean value;
final static MyBoolean TRUE=new MyBoolean(true), FALSE=new MyBoolean(false);
public MyBoolean(boolean b)
{
value=b;
}
public MyBoolean(String s)
{
if (s==null)
return null;
else if (s.equals("Y"))
return MyBoolean.TRUE;
else
return MyBoolean.FALSE;
}
public static String toString(MyBoolean b)
{
if (b==null)
return null;
else if (b.value)
return "Y";
else
reutrn "N";
}
public String toString()
{
return toString(this);
}
}
затем вы можете легко забрать логические значения из базы данных с помощью " myboolean flag=new MyBoolean (rs.getString ("flag")); "и запись в базу данных с помощью" rs.setString ("флаг", флаг.toString ());"
и конечно, вы можете добавить любую другую логику в класс, если у вас есть другие логические вещи, которые вам нужно сделать. Если для каких-то целей, которые вы хотите отобразить логические операторы как T/F или Да/нет или вкл/выкл или любой другой, вы можете просто добавить альтернативный метод toString вариантов -- toTFString или toString(значение,truetext,falsetext) или что-то вместо того, чтобы писать подобный код снова и снова.
вместо добавления столбца я предлагаю вам создать другую таблицу. Выслушать меня...
Предположим, у вас есть таблица с именем Customer
:
CREATE TABLE Customer
(
CustomerID NUMBER,
Name VARCHAR(100)
)
теперь предположим, что вы хотите указать, разрешено ли клиенту появляться в результатах поиска. Один из вариантов-добавить столбец, представляющий одно из двух возможных состояний:
CREATE TABLE Customer
(
CustomerID NUMBER,
Name VARCHAR(100),
Searchable BOOLEAN /* or CHAR(1) or BIT... */
)
ваш поисковый запрос будет выглядеть примерно так:
SELECT CustomerID, Name
FROM Customer
WHERE Name LIKE '%TheCustomerNameIAmLookingFor%'
AND Searchable = TRUE /* or 'Y' or 0... */
это мило и просто. Многие люди в этом потоке дают хорошие советы для выбора типа данных, которым должен быть этот столбец, чтобы синтаксис хорошо играл в различных базах данных.
альтернатива: создание отдельной таблице
вместо добавления другого столбца в Customer
, Я создам отдельную таблицу, которая хранит CustomerID
каждого клиента с возможностью поиска.
CREATE TABLE Customer
(
CustomerID NUMBER,
Name VARCHAR(100)
)
CREATE TABLE SearchableCustomer
(
CustomerID NUMBER
)
в этом случае клиент считается доступным для поиска, если их в SearchableCustomer
таблица. Запрос для поиска клиентов теперь становится:
SELECT CustomerID, Name
FROM Customer
WHERE Name LIKE '%TheCustomerNameIAmLookingFor%'
AND CustomerID IN (SELECT CustomerID FROM SearchableCustomer)
вы увидите, что эта стратегия очень переносима через RDBMSs:
- поиск клиентов с возможностью поиска использует
IN
пункт илиJOIN
- создание клиента для поиска использует
INSERT
сообщении - создание клиента без поиска использует
DELETE
сообщении
Сюрприз Пользу
вы бесплатно, чтобы сделать определение клиента для поиска, как тщательно, как вы хотите, если вы делаете SearchableCustomer
вид вместо таблицы:
CREATE VIEW SearchableCustomer AS
SELECT CustomerID
FROM Customer
WHERE Name LIKE 'S%' /* For some reason, management only cares about customers whose name starts with 'S' */
ваш запрос не меняется вообще! По моему опыту, это привело к огромной гибкости.
разрядные столбцы обычно используются для представления значений типа T/F или Y/N, по крайней мере, в SQL Server. Хотя пурист базы данных может сказать вам, что битовые столбцы не имеют места в базах данных, потому что они "слишком близки к оборудованию" - Joe Celko.
"выберите * От mytable Где col1
, что невозможно в SQL Server и Oracle (вам нужно иметь какой-то предикат или предикат)."
который идет только, чтобы показать, что смешной и смехотворный мерзость Oracle и SQL server на самом деле.
Если col1 объявлен типом BOOLEAN, то выражение "col1" IS предикат.
Если семантика предложения WHERE требует, чтобы его выражение просто вычисляет значение истины, и если какой-либо столбец объявлен типом "значение истины", то "где этот столбец" должен быть разрешен и поддерживаться. Период. Любая система, которая не просто выставляет своих авторов некомпетентными посредственными шарлатанами, которыми они являются.
Я обычно делаю это без битовых/булевых значений вообще. Вместо этого у меня было бы три стола. Допустим у нас есть система управления проектами, которая имеет проекты, и эти проекты имеют целую кучу атрибутов.
затем у нас есть таблицы:
Project - Project_ID (INT), - Name (VARCHAR) Attribute - Attribute_ID (INT), - Name (VARCHAR) ProjectAttribute_Rel - Project_ID (INT), - Attribute_ID (INT)
является ли атрибут проекта true или false зависит от того, есть ли для него строка в ProjectAttribute_Rel.
обычно вы имеете дело с Attribute_IDs в своем коде, поэтому, когда вы читаете атрибуты проекта (где у вас предположительно есть Project_ID), вы просто делаете (PHP произвольно используется в качестве примера):
$arrAttributes = array();
$oQuery = mysql_query('
SELECT Attribute_ID
FROM ProjectAttribute_Rel
WHERE Project_ID = '.addslashes($iProjectId).'
');
while ($rowAttribute = mysql_fetch_assoc($oQuery)) {
$arrAttributes[] = $rowAttribute['Attribute_ID'];
}
на этом этапе вы можете проверить, является ли атрибут проекта true, проверив, существует ли он в $arrAttributes вообще. В PHP это будет:
if (in_array($arrAttributes, $iAttributeId)) {
// Project attribute is true!
}
этот подход также позволяет делать всевозможные трюки, чтобы избежать перечисления множества атрибутов при обновлении, снова при выборе (потому что SELECT * плохо в коде), когда вы вставляете и так далее. Это связано с тем, что вы всегда можете перебирать атрибут таблицы, чтобы найти доступные атрибуты, поэтому, если вы добавляете один и делаете это таким образом, добавление/редактирование/удаление атрибутов тривиально. Скорее всего, ваш SQL даже не нужно будет изменять, потому что сами атрибуты определены в базе данных, а не в коде.
надеюсь, что это помогает.