Подтвердить что ты не робот

Должен ли я создать таблицу с первичным ключом varchar или int?

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

Я лично считаю, что привязка таблицы к фиксированной (max) длине varchar является no-no, потому что это означает, что она также должна распространять одну и ту же фиксированную длину в любых других таблицах, которые используют это как внешний ключ. Использование int не позволит использовать одну и ту же длину по всей доске, которая неизбежно приведет к ошибке человека, то есть 1 таблица имеет varchar (10), а другая varchar (20).

Это звучит как кошмар для первоначальной установки, плюс означает, что будущее поддержание таблиц также громоздко. Например, скажем, что ключевой столбец varchar внезапно становится 12 символами вместо 10. Теперь вам нужно пойти и обновить все остальные таблицы, которые могут быть огромными годами задач по линии.

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

4b9b3361

Ответ 1

Я бы определенно рекомендовал использовать поле INT NOT NULL IDENTITY(1,1) в каждой таблице, поскольку первичный ключ.

С полем IDENTITY вы можете позволить базе данных обрабатывать все детали, чтобы убедиться, что это действительно уникально и все, а тип данных INT - всего 4 байта и исправлен, поэтому он проще и удобнее использовать для первичной (и кластеризации) в вашей таблице.

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

Использование VARCHAR (10) или (20) просто использует слишком много места - 10 или 20 байт вместо 4, и то, что многие люди не знают, - значение ключа кластеризации будет повторяться на каждом отдельном индексе запись на каждом отдельном некластеризованном индексе на столе, поэтому потенциально вы тратите много места (не только на диск - это дешево, но и в основную память SQL Server). Кроме того, поскольку это переменная (может быть 4, может быть 20 символов), SQL-серверу труднее поддерживать хорошую структуру индекса.

Марк

Ответ 2

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

Первичные ключи являются логическими бизнес-элементами. Первичный ключ используется вашим приложением для идентификации объекта, а обсуждение первичных ключей в значительной степени зависит от использования естественных ключей или суррогатный ключ. Ссылки идут намного подробнее, но основная идея состоит в том, что естественные ключи производятся из существующего свойства объекта, такого как ssn или phone number, в то время как суррогатные ключи не имеют никакого значения в отношении бизнес-объекта, например id или rowid, и они обычно имеют тип IDENTITY или какой-то uuid. Мое личное мнение заключается в том, что суррогатные ключи превосходят естественные ключи, и выбор должен быть всегда значениями идентичности для локальных приложений, указаний для любых распределенных данных. Первичный ключ никогда не изменяется во время жизни объекта.

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

  • Типичный шаблон доступа к данным.
  • соображения хранения.

Шаблон доступа к данным. Под этим я понимаю способ, которым таблица запрашивается и обновляется. Помните, что кластеризованные ключи определяют фактический порядок строк в таблице. Для определенных шаблонов доступа некоторые макеты делают все различия в мире в отношении скорости запроса или для обновления согласованности:

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

  • Обработка стиля очереди FIFO. В этом случае таблица имеет два горячих точки: хвост, где возникают вставки (в очереди), и голова, где удаляются события (dequeue). Кластеризованный ключ должен учитывать это и организовывать таблицу, чтобы физически отделить местоположение хвоста и головы на диске, чтобы обеспечить совпадение между enqueue и dequeue, например. используя ключ заказа очереди. В чистых очередях этот кластеризованный ключ является единственным ключом, поскольку в таблице нет первичного ключа (он содержит сообщения, а не сущности). Но в большинстве случаев очередь не является чистой, она также действует как хранилище для сущностей, а линия между очередью и таблицей размывается. В этом случае есть также первичный ключ, который не может быть кластеризованным ключом: объекты могут быть повторно выставлены, тем самым изменяя значение кластеризованного ключа очереди в очереди, но они не могут изменить значение первичного ключа. Неспособность увидеть разделение является основной причиной того, что пользовательские таблицы, поддерживающие очереди, настолько печально трудны для правильного и пронизанного взаимоблокировками: поскольку в очереди и детекции происходит чередование, таблица вместо локализованных по хвосту и голове очереди.

  • Коррелированная обработка. Когда приложение хорошо спроектировано, он будет разбивать обработку взаимосвязанных элементов между своими рабочими потоками. Например, процессор рассчитан на 8 рабочих потоков (например, чтобы соответствовать 8 процессорам на сервере), поэтому процессоры разбивают данные между собой, например. работник 1 выбирает только учетные записи с именем A-E, работники 2 F-J и т.д. В таких случаях таблица должна быть фактически сгруппирована по имени учетной записи (или составным ключом, который имеет крайнее левое положение, первую букву имени учетной записи) так что рабочие локализуют свои запросы и обновления в таблице. Такая таблица будет иметь 8 различных горячих точек, вокруг области, в которой каждый рабочий концентрируется в данный момент, но важно то, что они не перекрываются (без блокировки). Этот вид дизайна распространен на высокопроизводительных OLTP-проектах и ​​в тестах TPCC, где этот вид разбиения также отражает в памяти расположение страниц, загруженных в буферный пул (NUMA), но я отвлекаюсь.

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

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

Итак, что мы делаем из всего этого? Всегда начинайте с кластеризованного ключа, который также является первичным ключом формы entity_id IDENTITY(1,1) NOT NULL. Разделите их и упорядочьте таблицу соответственно (например, раздел по дате), когда это будет одобрено.

Ответ 3

Я бы согласился с тем, что в большинстве случаев для большинства "нормальных" проектов баз данных используется тип поля INT (или идентификатор):

  • он не требует "алгоритма" для генерации id/key/value
  • у вас быстрые (er) соединения, и оптимизатор может работать намного сложнее по диапазонам и таким образом под капотом
  • вы следуете стандарту defacto

Тем не менее, вам также нужно знать ваши данные. Если вы собираетесь пробить подписанный 32-битный int, вам нужно подумать о unsigned. Если вы собираетесь взорвать это, возможно, 64-битные ints - это то, что вы хотите. Или, возможно, вам нужен UUID/хэш, чтобы упростить синхронизацию между экземплярами базы данных/осколками.

К сожалению, это зависит от YMMV, но я определенно использую int/identity, если у вас нет веских оснований.

Ответ 4

Как вы сказали, последовательность является ключевой. Я лично использую unsigned ints. Вы не собираетесь исчерпывать их, если вы не работаете с нелепыми объемами данных, и вы всегда можете знать, что любой ключевой столбец должен быть таким, и вам никогда не придется искать правильное значение для отдельных столбцов.

Ответ 5

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

INT

  • Используйте, если только не повод, чтобы этого не сделать.

GUID

  • Уникальность. Один пример - это случай, когда существует односторонняя связь между удаленными частями программы, а сторона, которая должна инициироваться, не является стороной с базой данных. В этом случае установка Guid на удаленной стороне безопасна, если выбрать INT нет.
  • Уникальность снова. Более отдаленный сценарий - это система, в которой несколько клиентов сосуществуют в отдельных базах данных, и происходит миграция между клиентами, такими как аналогичные пользователи, с помощью набора программ. Если этот пользователь подписывается на другую программу, их учетная запись пользователя может использоваться там без конфликтов. Другим сценарием является то, что клиенты приобретают объекты друг от друга. Если обе они находятся в одной системе, они часто ожидают, что миграция будет проще. По существу, любая частая миграция между клиентами.
  • Трудно использовать. Даже опытный программист не может запомнить руководство. При устранении неполадок часто бывает сложно копировать и вставлять идентификаторы для запросов, особенно если поддержка выполняется с помощью инструмента удаленного доступа. Гораздо проще постоянно ссылаться на SELECT * FROM Xxx WHERE ID = 7, чем SELECT * FROM Xxx WHERE ID = 'DF63F4BD-7DC1-4DEB-959B-4D19012A6306'

  • Индексирование - использование кластерного индекса для поля guid требует постоянной перестановки страниц данных и не столь эффективно индексировать как INT или даже короткие строки. Он может убить производительность - не делайте этого.

CHAR

  • Читаемость. Хотя общепринятая мудрость заключается в том, что никто не должен находиться в базе данных, реальность систем заключается в том, что у людей будет доступ - надеюсь, персонал вашей организации. Когда эти люди не разбираются в синтаксисе соединения, нормализованная таблица с ints или guid не понятна без многих других запросов. Такая же нормализованная таблица с НЕКОТОРЫМИ строковыми клавишами может быть гораздо более пригодной для устранения неполадок. Я склонен использовать это для типа таблицы, где я поставляю записи во время установки, чтобы они не менялись. Такие вещи, как StatusID на главной таблице, гораздо удобнее для поддержки, когда ключ "Закрыт" или "Ожидает", чем цифра. Использование традиционных ключей в этих областях может превратить легко разрешенную проблему в то, что требует помощи разработчика. Узкие места, как это плохо, даже когда это вызвано тем, что позволяет сомнительному персоналу получить доступ к базе данных.
  • Constrain. Даже если вы используете строки, сохраните их фиксированную длину, которая ускоряет индексирование и добавляет ограничение или внешний ключ, чтобы сохранить мусор. Иногда использование этой строки позволяет удалить таблицу поиска и сохранить выбор в виде простого Enum в коде - все равно важно ограничить данные, поступающие в это поле.

Ответ 6

Для лучшей производительности, 99,999% времени, когда первичный ключ должен быть единственным целым полем.

Если вам не требуется, чтобы первичный ключ был уникальным для нескольких таблиц в базе данных или в нескольких базах данных. Я предполагаю, что вы спрашиваете о MS SQL Server, потому что именно так был помечен ваш вопрос. В этом случае вместо этого используйте поле GUID. Хотя лучше, чем varchar, производительность поля GUID не так хороша, как целое число.

Ответ 7

Используйте INT. Ваши баллы действительны; Я бы определил приоритет как:

  • Простота использования возможности автоматического наращивания SQL - зачем изобретать колесо?
  • Managability - вы не хотите менять ключевое поле.
  • Производительность
  • Дисковое пространство

1 и 2 требуется время разработки/энергия/усилие. 3 и 4 вы можете бросить оборудование на.

Ответ 8

Если бы Джо Селко был здесь, у него были бы суровые слова...; -)

Я хочу указать, что INTs как жесткое и быстрое правило не всегда уместно. Скажем, у вас есть стол для автомобилей со всеми видами грузовиков и т.д. Теперь скажите, что у вас есть таблица VehicleType. Если вы хотите получить все грузовики, вы можете это сделать (с семенем идентификации INT):

SELECT V.Make, V.Model
FROM Vehicle as V
INNER JOIN VehicleType as VT
ON V.VehicleTypeID = VT.VehicleTypeID
WHERE VT.VehicleTypeName = 'Truck'

Теперь, с Varchar PK на VehicleType:

SELECT Make, Model
FROM Vehicle 
WHERE VehicleTypeName = 'Truck'

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

Просто мысль.: -)

Ответ 9

Хотя рекомендуется INT, это действительно зависит от вашей ситуации.

Если вы заинтересованы в ремонтопригодности, тогда возможны и другие типы. Например, вы могли бы эффективно использовать Guid в качестве первичного ключа. Есть причины не делать этого, но согласованность не одна из них.

Но да, если у вас нет веских оснований, int является самым простым в использовании и наименее вероятным вызовом каких-либо проблем.

Ответ 10

С PostgreSQL я обычно использую "Serial" или "BigSerial" тип данных для генерации первичных ключей. Значения автоматически увеличиваются, и я всегда считаю, что целые числа легко работать. Они по существу эквивалентны целочисленному полю MySQL, которое установлено на "auto_increment".

Ответ 11

Нужно подумать над тем, достаточно ли 32-битного диапазона для того, что вы делаете. Идентификаторы статуса Twitter были 32-битными INT, и у них были проблемы, когда они закончились.

Использовать BIGINT или UUID/GUID в этой ситуации является дискуссионным, и я не являюсь хардкорным пользователем базы данных, но UUID можно хранить в VARCHAR фиксированной длины, не беспокоясь о том, что вам нужно будет изменить поле размер.

Ответ 12

Мы должны иметь в виду, что первичный ключ таблицы не должен иметь "бизнес-логику", и он должен быть только личностью записи, к которой он принадлежит. Следуя этому простому правилу, int и особенно идентификатор int - очень хорошее решение. Если спросить о varchar, я предполагаю, что вы имеете в виду использование, например, "Полное имя" в качестве ключа к таблице "люди". Но что, если мы хотим изменить имя от "George Something" до "George A. Something"? И какой размер будет полем? Если мы изменим размер, мы также должны изменить размер на всех внешних таблицах. Поэтому нам следует избегать логики на клавишах. Иногда мы можем использовать социальный идентификатор (целочисленное значение) в качестве ключа, но я тоже этого избегаю. Теперь, если у проекта есть перспективы для расширения, вы должны также использовать Гиды (тип uniqueidentifier SQL).

Ответ 13

Имея в виду, что это довольно старый вопрос, я все же хочу сделать дело для использования varchar с суррогатными ключами для будущих читателей:

  • Среда с несколькими реплицируемыми машинами
  • Сценарии, в которых требуется, чтобы идентификатор вставленной строки был известен, прежде чем он фактически вставлен (то есть клиент назначает этот идентификатор, а не базу данных).