Дизайн базы данных и использование нечисловых первичных ключей - PullRequest
14 голосов
/ 29 мая 2009

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

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

--
-- website
--
CREATE TABLE IF NOT EXISTS `website` (
  `name` varchar(126) NOT NULL,
  `client_id` int(11) NOT NULL,
  `date_created` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `notes` text NOT NULL,
  `website_status` varchar(26) NOT NULL,
  PRIMARY KEY  (`name`),
  KEY `client_id` (`client_id`),
  KEY `website_status` (`website_status`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- website_status
--
CREATE TABLE IF NOT EXISTS `website_status` (
  `name` varchar(26) NOT NULL,
  PRIMARY KEY  (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `website_status` (`name`) VALUES
('demo'),
('disabled'),
('live'),
('purchased'),
('transfered');

--
-- client
--
CREATE TABLE IF NOT EXISTS `client` (
  `id` int(11) NOT NULL auto_increment,
  `date_created` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `client_status` varchar(26) NOT NULL,
  `firstname` varchar(26) NOT NULL,
  `lastname` varchar(46) NOT NULL,
  `address` varchar(78) NOT NULL,
  `city` varchar(56) NOT NULL,
  `state` varchar(2) NOT NULL,
  `zip` int(11) NOT NULL,
  `country` varchar(3) NOT NULL,
  `phone` text NOT NULL,
  `email` varchar(78) NOT NULL,
  `notes` text NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `client_status` (`client_status`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

--
-- client_status
---
CREATE TABLE IF NOT EXISTS `client_status` (
  `name` varchar(26) NOT NULL,
  PRIMARY KEY  (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `client_status` (`name`) VALUES
('affiliate'),
('customer'),
('demo'),
('disabled'),
('reseller');

Как видите, 3 из 4 таблиц используют свое «имя» в качестве первичного ключа. Я знаю, что они всегда будут уникальными. В 2 случаях (таблицы * _status) я в основном использую динамическую замену ENUM, поскольку параметры состояния могут измениться в будущем, а для таблицы «сайт» я знаю, что «имя» сайта будет всегда быть уникальным.

Мне интересно, является ли это разумной логикой, избавиться от идентификаторов таблиц, когда я знаю, что имя всегда будет уникальным идентификатором или рецептом катастрофы? Я не опытный администратор баз данных, поэтому любые отзывы, критические замечания и т. Д. Были бы чрезвычайно полезны.

Спасибо, что нашли время, чтобы прочитать это!

Ответы [ 11 ]

16 голосов
/ 29 мая 2009

Есть 2 причины, по которым я всегда добавляю идентификационный номер в таблицу поиска / ENUM:

  1. Если вы ссылаетесь на таблицу из одного столбца с именем, тогда вам лучше будет использовать ограничение
  2. Что произойдет, если вы захотите переименовать одну из записей client_status? например если вы хотите изменить имя с «аффилированного» на «аффилированный пользователь», вам необходимо обновить таблицу клиентов, в этом нет необходимости. Идентификационный номер служит ссылкой, а имя - описанием.

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

EDIT: Как указано выше, у вас возникнут проблемы, если имя веб-сайта будет переименовано. Делая это первичным ключом, вам будет очень трудно, если не невозможно, изменить его на более поздний срок.

13 голосов
/ 29 мая 2009

При изготовлении натуральных PRIMARY KEY убедитесь, что их уникальность находится под вашим контролем.

Если вы абсолютно уверены, что у вас никогда не будет нарушения уникальности, тогда можно использовать эти значения как PRIMARY KEY.

Поскольку website_status и client_status, по-видимому, генерируются и используются вами и только вами, их можно использовать как PRIMARY KEY, хотя наличие длинного ключа может повлиять на производительность.

website кажется, что имя находится под контролем внешнего мира, поэтому я бы сделал его простым полем. Что если они захотят переименовать свои website?

Контрпримеры будут кодами SSN и ZIP: их генерирует не вы, и нет никакой гарантии, что они никогда не будут продублированы.

9 голосов
/ 29 мая 2009

У Кимберли Триппа есть отличная серия статей в блогах ( GUID в качестве ПЕРВИЧНЫХ КЛЮЧЕЙ и / или ключа кластеризации и Дебаты по кластерному индексу продолжаются ) по вопросу создания кластерных индексов и выбора первичного ключа (связанные проблемы, но не всегда точно такие же). Она рекомендует, чтобы кластерный индекс / первичный ключ был:

  1. Уникальный (в противном случае он бесполезен в качестве ключа)
  2. Узкий (ключ используется во всех некластеризованных индексах и в отношениях внешнего ключа)
  3. Статический (вам не нужно изменять все связанные записи)
  4. Всегда увеличивается (поэтому новые записи всегда добавляются в конец таблицы, и их не нужно вставлять в середину)

Использование «Имени» в качестве ключа, хотя оно, кажется, удовлетворяет # 1, не удовлетворяет ЛЮБОМУ из трех других.

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

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

3 голосов
/ 29 мая 2009

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

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

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

например. если в вашей таблице 10 миллионов строк, 10 некластеризованных индексов, а ключ кластеризации составляет 26 байтов вместо 4 (для INT), то вы тратите 10 миллионов. на 10 на 22 байта в общей сложности 2,2 миллиарда байтов (или примерно 2,2 гигабайта) - это уже не арахис!

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

Марк

2 голосов
/ 03 июля 2009

"Если вы абсолютно уверены, что у вас никогда не будет нарушения уникальности, тогда можно использовать эти значения в качестве ПЕРВИЧНОГО КЛЮЧА."

Если вы абсолютно уверены, что у вас никогда не будет нарушения уникальности, не пытайтесь определить ключ.

1 голос
/ 27 декабря 2012

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

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

1 голос
/ 30 мая 2009

Я бы сказал, что база данных, устойчивая к повреждениям, даже если она работает немного медленнее, лучше, чем та, которая не является & t;

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

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

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

1 голос
/ 29 мая 2009

Это просто кажется очень плохой идеей. Что если вам нужно изменить значение перечисления? Идея состоит в том, чтобы сделать его реляционной базой данных, а не набором простых файлов. На этом этапе, почему есть таблица client_status? Более того, если вы используете данные в приложении, используя тип, такой как GUID или INT, вы можете проверить тип и избежать неверных данных (в том числе и при проверке типа). Таким образом, это еще одна из многих линий, сдерживающих взлом.

1 голос
/ 29 мая 2009

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

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

0 голосов
/ 24 апреля 2018

Вы НИКОГДА не знаете, когда компания, на которую вы работаете, внезапно развивается, и вам приходится нанимать 5 разработчиков за ночь. Лучше всего использовать числовые (целочисленные) первичные ключи, поскольку всей команде будет намного проще работать с AND, и это повысит вашу производительность в случае роста базы данных. Если вам нужно разбить записи и разбить их на части, вы можете использовать первичный ключ. Если вы добавляете записи с отметкой даты и времени (как и в каждой таблице), и где-то в коде есть ошибка, которая неправильно обновляет это поле, единственный способ проверить, была ли запись введена в правильной последовательности, это проверить основной ключи. Вероятно, есть еще 10 причин TSQL или отладки для использования первичных ключей INT, не в последнюю очередь из которых пишется простой запрос для выбора последних 5 записей, введенных в таблицу.

...