Должен ли я добавить поле автоинкремента / IDENTITY в таблицу перекрестных ссылок только для ПК?

9

Я добавляю следующую таблицу перекрестных ссылок в базу данных, размещенную на SQL Server:

company_id bigint not null (FK)
org_path nvarchar (2048) not null

company_idПоле относится к idобласти в другую таблицу , в которой (это первичный ключ).

Учитывая, что также может быть несколько записей с одинаковыми значениями company_id, любой первичный ключ должен использовать оба поля. Однако я не могу создать ключ, используя оба поля, потому что org_pathон слишком длинный для SQL Server.

Что касается org_path, это единственная таблица, в которой он существует. Есть большая вероятность, что запросы к этой таблице будут запрашивать либо все записи, либо все org_pathзаписи company_id. Или, другими словами, кажется сомнительным, что эта таблица когда-нибудь будет запрошена org_path. Кроме того, маловероятно, что оно org_pathбудет обновлено, и, скорее всего, будет вставлено и, вероятно, редко - удалено.

Я ожидаю, что общее количество строк будет в тысячах.

Кроме того, причина в nvarchar (2048)том, что значение должно имитировать это в сторонних БД. Типичным примером будет что-то вроде

\Translation Providers\[customer name]\[order name]\

и может содержать диакритические знаки.

Поэтому мой вопрос заключается в следующем: было бы более эффективно добавить idполе автоинкремента и использовать его вместе с company_idпервичным ключом или добавить ненужные издержки - и имеет ли тот факт, что company_idпервичный ключ в другой таблице имеет какой-либо эффект здесь?

AWJ
источник

Ответы:

7

Для неуникального кластеризованного индекса в comany_idодиночку SQL Server автоматически добавит 4-байтовый целочисленный уникализатор ко всем дублирующим (т.е. вторым и последующим для значения ключа) ключам кластеризованного индекса, чтобы сделать его уникальным. Это не доступно пользователю.

Преимущество добавления собственного уникального идентификатора в качестве столбца вторичного ключа заключается в том, что вы все равно можете выполнять company_idпоиск по отдельным строкам, а также выполнять поиск по ним более эффективно (используя company_id, identitycolвместо company_idостаточного предиката org_path). Тогда кластеризованный индекс будет уникальным company_id, identitycol, поэтому скрытые уникализаторы добавляться не будут.

Кроме того, если у вас есть дубликаты для (company_id,org_path), наличие столбца явных идентификаторов (своего рода «выставленный уникализатор») облегчит выбор только одного из них для удаления или обновления.

Мартин Смит
источник
12

Следует учитывать, что первичный ключ и кластерный индекс - это не одно и то же. Первичный ключ является ограничением и имеет дело с правилами, по которым живут данные (т. Е. Целостность данных); это не имеет ничего общего с эффективностью / производительностью. Первичный ключ требует, чтобы ключевые столбцы были уникальными (в комбинации) и НЕ ПУСТО (отдельно). PK применяется посредством уникального индекса, хотя он может быть кластеризованным или некластеризованным.

Кластерный индекс - это средство физического (т. Е. На диске) упорядочения данных в таблице и определения производительности; это не имеет ничего общего с целостностью данных. Кластерный индекс можеттребует, чтобы ключевые столбцы были уникальными (в комбинации), но это не обязательно. Однако, поскольку кластеризованный индекс является физическим порядком данных, он должен однозначно идентифицировать каждую строку независимо от того, что именно. Поэтому, если вы не установите для него уникальность, он создаст собственную уникальность через скрытый 4-байтовый столбец «уникальность». Этот столбец всегда присутствует в неуникальных кластеризованных индексах, но он не занимает места, когда ключевые поля уникальны (в комбинации). Чтобы собственными глазами увидеть, как работает этот столбец «уникальность» (как в кластеризованном индексе, так и в воздействии на некластеризованные индексы), пожалуйста, ознакомьтесь с этим тестовым сценарием, который я разместил на PasteBin: сценарий T-SQL, чтобы проверить размер Uniquifier .

Отсюда и главный вопрос:

Было бы более эффективно добавить idполе автоинкремента и использовать его вместе с company_idпервичным ключом или добавить ненужные накладные расходы

объединяет эти два понятия, поэтому их необходимо рассматривать отдельно, хотя определенно есть некоторые совпадения.

Должен ли IDENTITYбыть добавлен столбец или это будет ненужными накладными расходами?

Если вы добавляете INT IDENTITYстолбец и используете его для создания PK, предполагая, что это будет Clustered PK, это добавляет 4 байта к каждой строке. Этот столбец видим и может использоваться в запросах. Его можно добавить в другие таблицы в качестве внешнего ключа, хотя в данном конкретном случае этого не произойдет.

Если вы не добавите INT IDENTITYстолбец, вы не сможете создать PK для этой таблицы. Тем не менее, вы все равно можете создать кластеризованный индекс в таблице, если вы не используете эту UNIQUEопцию. В этом случае SQL Server добавит скрытый столбец с именем «uniquifier», который ведет себя так, как описано выше. Поскольку столбец скрыт, его нельзя использовать в запросах или в качестве ссылки для внешних ключей.

Таким образом, что касается эффективности, эти варианты примерно одинаковы. Да, при использовании неуникального кластеризованного индекса будет немного меньше места из-за того, что некоторые строки (с исходными значениями уникальных ключей) занимают 0 байтов, в то время как все строки в IDENTITY/ PK будут занимать 4 байта. Но 0-байтовых строк будет недостаточно (особенно с ожидаемым небольшим количеством строк), чтобы когда-либо заметить разницу, не говоря уже о том, чтобы перевесить удобство использования IDстолбца в запросах.

Столбец INT IDENTITY или хэш org_pathсохраняемого вычисляемого столбца?

Учитывая, что вы не будете искать строки, основанные на org_pathзначениях, нет смысла добавлять накладные расходы для сохраняемого вычисляемого столбца, а также необходимость вычислять этот хэш в запросах для сопоставления с вычисляемым столбцом (это был мой оригинальное предложение, доступное в истории изменений здесь , которое было основано на первоначальной формулировке / деталях Вопроса). В этом конкретном случае INT IDENTITYстолбец «ID», вероятно, является лучшим.

Порядок ключевых столбцов

Учитывая, что IDстолбец будет редко, если вообще когда-либо использоваться в запросах, и учитывая, что два основных сценария использования - это получение «всех строк» ​​или «всех строк для данного company_id», я бы создал PK на company_id, id. А поскольку это означает, что строки не вставляются последовательно, я бы указал значение, FILLFACTORравное 90. Вам также нужно будет регулярно выполнять обслуживание индекса, чтобы уменьшить фрагментацию.

Второй вопрос

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

Нет.

Вызывать

Так как org_pathзначения внутри company_idявляются уникальными, вы все равно должны создать Триггер, INSERT, UPDATEчтобы применить это. В триггере выполните запрос IF EXISTSс запросом, который, вероятно, выполняет операции a COUNT(*)и GROUP BY company_id, org_path. Если что-то найдено, выполните ROLLBACKкоманду a для отмены операции DML, а затем RAISERRORукажите, что есть дубликаты.

сличение

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

Соломон Руцкий
источник
Давайте продолжим эту дискуссию в чате .
Соломон Руцкий
0

Зачем тебе ПК?

Почему бы просто не использовать company_id как некластеризованный индекс?

Вы сказали, что чаще всего ищут все записи или by company_id
Редко обновляют
Редко удаляют
org_path, это единственная таблица, в которой он существует

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

Если вас беспокоит DRI, таблицы должны использовать таблицу Company в качестве FK для company_id

папараццо
источник
Привет. Что касается « Почему бы просто не пойти с company_id в качестве некластеризованного индекса? »: Потому что это будет иметь 2 недостатка : 1) это будет еще 1 вещь, занимающая место, тогда как кластерный индекс является таблицей, поэтому нет дополнительного элемента, и 2) для получения поля NVARCHAR все равно потребуется поиск RID, если только это не INCLUDEстолбец, но это еще хуже, поскольку он просто дублирует таблицу. Правда, ПК не нужен; Важной частью является кластерный индекс. Но как только у вас появится ИДЕНТИЧНОСТЬ, вы можете пойти с ПК. И, пожалуйста, смотрите новую ссылку в моем ответе для ознакомления с Uniquifier 😃
Соломон Руцки
@srutzky Но он избегает 4-байтового целочисленного уникализатора, так что я вижу это как мойку
папарацци
С менее чем 10 тысячами строк это не имеет значения; вам, вероятно, нужно быть в миллионах строк, прежде чем вы заметите эффект всего 4 байта. Таким образом, для запроса «получить все строки» нет никакой разницы в любом из этих параметров. Но для запроса "get for company_id = @param" физическое упорядочение данных по company_id поможет, особенно если нет необходимости выполнять поиск RID для каждой строки.
Соломон Руцки
@srutzky Wash - это стирка - 10K или 1G. Это просто что-то для рассмотрения OP.
папараццо