Что может быть недостатком в том, чтобы всегда иметь один целочисленный столбец в качестве первичного ключа?

18

В рамках одного веб-приложения, над которым я работаю, все операции с базой данных абстрагируются с использованием некоторых общих репозиториев, определенных в Entity Framework ORM.

Однако, чтобы иметь простой дизайн для общих репозиториев, все задействованные таблицы должны определять уникальное целое число ( Int32в C #, intв SQL). До сих пор это всегда был ПК таблицы, а также IDENTITY.

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

Прикладной уровень обычно выполняет следующие операции:

  • начальная загрузка данных из таблицы (*) -SELECT * FROM table
  • Обновление -UPDATE table SET Col1 = Val1 WHERE Id = IdVal
  • Удалить -DELETE FROM table WHERE Id = IdVal
  • Вставить -INSERT INTO table (cols) VALUES (...)

Менее частые операции:

  • Массовая вставка - с BULK INSERT ... into tableпоследующей (*) загрузкой всех данных (для получения сгенерированных идентификаторов)
  • Массовое удаление - это обычная операция удаления, но «громоздкая» с точки зрения ORM:DELETE FROM table where OtherThanIdCol = SomeValue
  • Массовое обновление - это обычная операция обновления, но «громоздкая» с точки зрения ORM:UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue

* все небольшие таблицы кэшируются на уровне приложения и почти все SELECTsне доходят до базы данных. Типичным шаблоном является начальная загрузка и множество INSERTs, UPDATEs и DELETEs.

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

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

[РЕДАКТИРОВАТЬ]

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

  1. Текущая специфика приложения - я не упомянул о текущем веб-приложении, потому что хочу понять, можно ли повторно использовать модель и для других приложений. Тем не менее, мой частный случай - это приложение, которое извлекает много метаданных из DWH. Исходные данные довольно грязные (странным образом денормализованы, имеют некоторые несоответствия, во многих случаях нет естественного идентификатора и т. Д.), И мое приложение генерирует четко разделенные сущности. Также IDENTITYотображаются многие из сгенерированных идентификаторов ( ), чтобы пользователь мог использовать их в качестве бизнес-ключей. Это, помимо масштабного рефакторинга кода, исключает использование GUID .

  2. «они не должны быть единственным способом однозначно идентифицировать ряд» (Аарон Бертран ♦) - это очень хороший совет. Все мои таблицы также определяют УНИКАЛЬНОЕ ОГРАНИЧЕНИЕ, чтобы гарантировать, что бизнес-дубликаты не допускаются.

  3. Дизайн, ориентированный на внешние приложения, и дизайн, основанный на базе данных - выбор дизайна обусловлен этими факторами

    1. Ограничения Entity Framework - допускается использование нескольких столбцов PK, но их значения не могут быть обновлены

    2. Пользовательские ограничения - наличие единого целочисленного ключа значительно упрощает структуры данных и код, отличный от SQL. Например: все списки значений имеют целочисленную клавишу и отображаемые значения. Что еще более важно, это гарантирует, что любая таблица, помеченная для кэширования, сможет быть помещена в Unique int key -> valueкарту.

  4. Сложные запросы на выборку - это почти никогда не произойдет, потому что данные всех небольших таблиц (<20-30K записей) кэшируются на уровне приложения. Это немного усложняет жизнь при написании кода приложения (сложнее писать LINQ), но база данных гораздо лучше:

    1. Представления списка - не будут генерировать SELECTзапросы при загрузке (все кэшируется) или запросы, которые выглядят так:

      SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)

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

    2. Редактировать представления - сгенерирует SELECTутверждения вроде этого:

      SELECT allcolumns FROM BigTable WHERE PKId = value1

(все фильтры и значения ints)

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

Ответы:

19

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

Я возражаю против слепого добавления их к каждой таблице в сообщении блога за 2010 год:

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

Аарон Бертран
источник
Спасибо за быстрый ответ. Да, приложение использует ORM (EF). Он не требует ключей столбцов с одним целым числом, но я ввел это ограничение, чтобы сделать некоторые общие операции намного проще (с точки зрения дизайна). Кроме того, все кэши приложений хранят все в картах (словарях) для быстрого поиска по ключу, и ключ должен быть уникальным. Поскольку я выбрал целые числа вместо направляющих, я вынужден использовать IDENTITY для любой таблицы, в которую вставляю. Для таблиц с фиксированными значениями IDENTITY не требуется.
Алексей
Я думаю, что существуют случаи, когда необходимо избегать проверки уникальности естественных ключей. Как тот, кто работает с данными ГИС, тот, кто сразу приходит на ум, - это тот, где естественным ключом является либо сама геометрия, либо геометрия плюс некоторый внешний ключ. Поиск точной геометрии всегда будет непрактичным, поэтому ограничение уникальности вряд ли сильно поможет и может иметь недостатки в производительности. То же самое может быть правдой, если часть естественного ключа представляет собой длинный текстовый столбец. Но я согласен: да, когда это практически возможно, следует применять уникальное ограничение на естественный ключ.
jpmc26
13

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

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

Затем, с точки зрения администратора баз данных, вещи, которые делают БД медленной или раздутой, безусловно, не 4 байта (или что-то еще) в строке, а такие вещи, как неправильные или отсутствующие индексы, забытые реорганизации таблиц / индексов, неправильные параметры настройки ОЗУ / табличного пространства пренебрегая использованием переменных связывания и так далее. Они могут замедлить работу БД в 10, 100, 10000 раз, а не в дополнительном столбце идентификаторов.

Таким образом, даже если бы имелся технический, измеримый недостаток наличия дополнительных 32 битов на строку, вопрос не в том, можете ли вы оптимизировать идентификатор, а в том, будет ли идентификатор необходим в какой-то момент, что будет более скорее всего, чем нет. И я не собираюсь пренебрегать всеми «мягкими» выгодами от позиции разработки программного обеспечения (как, например, ваш пример ORM или тот факт, что это облегчает разработчикам программного обеспечения, когда все идентификаторы в проекте имеют одинаковый тип данных и т. Д.) ,

Примечание: обратите внимание, что вам не нужен отдельный идентификатор для n:mтаблиц ассоциации, потому что для таких таблиц идентификаторы связанных объектов должны формировать первичный ключ. Контрпример - странная n:mассоциация, которая допускает множественные ассоциации между одними и теми же двумя сущностями по любой причудливой причине - тогда для создания PK им потребуется собственный столбец ID. Однако есть библиотеки ORM, которые не могут обрабатывать многоколоночные PK, поэтому это может быть причиной для снисхождения к разработчикам, если им приходится работать с такой библиотекой.

Anoe
источник
2
«странная ассоциация n: m, которая допускает множественные ассоциации между одними и теми же сущностями», ОЧЕНЬ распространенная в реальной жизни Например, человек владеет автомобилем, затем требования изменяются на учетные, когда право собственности начиналось и заканчивалось (человек может продать автомобиль и выкупить его позже, а также разбить ваше программное обеспечение ....)
Ян
Да, что-то в этом роде, @IanRingrose.
AnoE
6

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

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

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

nvogel
источник
1
И все же многие системы имеют синтетические целочисленные первичные ключи в каждой таблице (например, почти в каждом приложении Ruby on Rails, когда-либо написанном), не страдая от таких проблем. Они также никогда не сталкиваются с проблемой необходимости вносить изменения в первичные ключи (которые никогда не должны были происходить) во все таблицы внешних ключей.
Дэвид Олдридж
2
Вопрос задал возможные недостатки, отсюда и мой ответ. Я не отрицаю, что суррогатные ключи могут иметь смысл, если их использовать с умом. Но я видел таблицы с 3,4,5 (или многими другими) бессмысленными внешними ключами, поэтому для получения полезных результатов от них требовалось 3,4,5 или более соединений. Более прагматичный дизайн, возможно, не потребовал вообще никаких соединений.
nvogel
1
Я не уверен, что именно выполнение таких запросов является основной проблемой, с которой сталкиваются люди с таким дизайном, - это написание запроса, на который они часто возражают.
Дэвид Олдридж
5

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

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

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

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

СаМ
источник
2
Звучит как ужасное оправдание, чтобы поменять все ключи на направляющие. В настоящее время я работаю с базой данных, которая использует направляющие для всех суррогатных ключей .. это не весело.
Энди
2
Нет. Использование GUID - это не весело. Они мне не нравятся, но я уважаю их ценность в определенных случаях использования.
СаМи
2

Положить в сторону:

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

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

TH
источник
4
Я не могу вспомнить ни одного случая, когда соединение по натуральному ключу было бы более эффективным, чем соединение по целому числу - не так много натуральных ключей может быть меньше 4 байтов, и если они есть, не может быть достаточно уникального строки, чтобы сделать материал разницы.
Аарон Бертран
Для компетентного, оптимизируемого SQL я согласен, но я имел в виду возможные ограничения генераторов SQL. Мой единственный опыт в этой области заключается в том, что меня просили создать расширенные представления, с помощью которых EF можно было бы кормить ложкой - хотя, возможно, разработчики .net не знали достаточно об EF или по другим причинам.
TH
@AaronBertrand Я бы сказал, что единственный способ, которым они могут быть более эффективными, - это если объединение вообще не нужно. Единственное место, где я рассматриваю использование естественных ключей, - это стандартные списки кодов, такие как коды валют ISO4127 (которые могут распознаваться человеком), и я мог бы использовать GBP, EUR и т. Д. В качестве внешнего ключа для первичного или альтернативного ключа в коде валюты. стол.
Дэвид Олдридж
@ Давид Конечно, я говорил о случаях, когда необходимы объединения. Во многих случаях я не хочу, чтобы естественный ключ распространялся по всем связанным таблицам, потому что естественные ключи могут изменяться, и это болезненная вещь.
Аарон Бертран
Хммм, я понимаю, как мой ответ может быть неправильно истолкован как продвижение естественных внешних ключей вместо суррогата. Чтобы было ясно, я фактически упомянул их, потому что а) я прочитал вопрос Алексея как «это проблема, что мы не используем естественные ключи?», Б) заключительный вопрос Алексея начался с «с точки зрения администратора баз данных», и я чувствовал, что должен как бы признать, что есть более чем одна перспектива и в) потому что я думаю, что функции ORM, которые будут использоваться, в значительной степени диктуют выбор (если это действительно может иметь значение). Я твердо в лагере суррогатных ключей.
TH
2

У вас есть несколько факторов, которые помогут вам,

  1. Определение и спецификация

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

  2. Уникальность.

    Для личной гигиены, объединений и высокоуровневых функций базы данных вам потребуется: (а) уникальный столбец, (б) уникальный ряд столбцов

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

  3. Внедрение и оптимизация.

    Int имеет тенденцию быть небольшой формой данных, быстрой для сравнения и равенства. Сравните это со строкой Unicode, чьи параметры сортировки могут зависеть от локали (местоположение и язык). Сохранение 4242 в строке ASCII / UTF8 занимает 4 байта. Сохраняя его как целое число, он умещается в 2 байта.

Поэтому, когда дело доходит до недостатков, у вас есть несколько факторов.

  1. Путаница и двусмысленность.

    1. @ Аарон Бертран в записи блога хорошо это подводит. Самостоятельно не документировать наличие OrderID по спецификации и задаче, а затем навязывать « OrderID » через реализацию базы данных. Иногда вам нужно уточнить это или создать соглашение, но это может добавить путаницы.
  2. Космос.

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

  3. Кластеризация.

    Вы можете заказать данные только одним способом. Если вы вводите суррогатный ключ, который вам не нужен, вы кластеризуете этот путь или путь естественного ключа?

Эван Кэрролл
источник
Хорошие и короткие плюсы и минусы.
Алексей
@ Alexei спасибо, рассмотрите возможность пометить его как выбранный, если он соответствует тому, что вы ищете. Или просить разъяснений.
Эван Кэрролл