Я занимаюсь разработкой базы данных SQL Server 2012 и у меня есть сомнения по поводу столбцов nvarchar в качестве первичных ключей.
У меня есть эта таблица:
CREATE TABLE [dbo].[CODES]
(
[ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
[CODE_LEVEL] [tinyint] NOT NULL,
[CODE] [nvarchar](20) NOT NULL,
[FLAG] [tinyint] NOT NULL,
[IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED
(
[CODE_LEVEL] ASC,
[CODE] ASC
)
)
Но теперь я хочу использовать [CODE]
столбец в качестве первичного ключа и удалить [ID_CODE]
столбец.
Есть ли проблема или штраф, если у меня есть NVARCHAR
столбец как PRIMARY KEY
?
[CODE]
значение столбца должно быть уникальным, поэтому я подумал, что могу установить UNIQUE
ограничение для этого столбца.
Нужно ли использовать в [CODE]
качестве первичного ключа, или лучше установить UNIQUE
ограничение на [CODE]
столбец?
sql-server
primary-key
unique-constraint
VansFannel
источник
источник
CODE
столбец должен быть уникальным, а не первичным ключом. Я подозреваю, что это несет информацию. Если эта информация каким-либо образом изменяема, то выCODE
должны изменить или устареть. Это сделало бы ваш Первичный Ключ изменчивым, и я не вижу, чтобы это хорошо заканчивалось. Лучше всего, чтобы ваш ПК был просто ключом, а ваш КОД мог делать то, что ему нравится. Просто мнение.Ответы:
Да, безусловно, есть отрицательные последствия использования строки вместо числового типа для первичного ключа, и даже более того, если этот PK является кластеризованным (что в действительности имеет место в вашем случае). Тем не менее, степень, в которой вы видите эффект (ы) использования строкового поля, является функцией от a) сколько строк в этой таблице и b) сколько строк в других таблицах имеют внешний ключ к этому PK. Если у вас есть только 10 тыс. Строк в этой таблице и 100 тыс. Строк в нескольких других таблицах, которые передаются в эту таблицу через это поле, то, возможно, это будет не так заметно. Но эти эффекты, безусловно, становятся более заметными по мере увеличения числа строк.
Необходимо учитывать, что поля в кластеризованном индексе переносятся в некластеризованные индексы. Таким образом, вы смотрите не только до 40 байтов на строку, но и (40 * some_number) байтов. И в любых таблицах FK у вас есть те же 40 байтов в строке, и чаще всего в этом поле будет некластеризованный индекс, так как он используется в JOIN, поэтому теперь он действительно удваивается в любых таблицах, для которых FK используется. вот этот. Если кто-то склонен думать, что 40 байтов * 1 миллион строк * 10 копий этого документа не о чем беспокоиться, см. Мою статью « Диск дешев»! ORLY? которая подробно описывает все (или, по крайней мере, большинство) областей, на которые повлияло это решение.
Еще одна вещь, которую следует учитывать, это то, что фильтрация и сортировка по строкам, особенно когда не используется двоичное сопоставление (я предполагаю, что вы используете базу данных по умолчанию, которая обычно нечувствительна к регистру), гораздо менее эффективна (т. Е. Занимает больше времени), чем при использовании
INT
/BIGINT
. Это влияет на все запросы, которые фильтруют / объединяют / сортируют в этом поле.Следовательно, использование чего-то подобного
CHAR(5)
, вероятно, будет хорошо для кластерного PK, но в основном, если оно также было определено сCOLLATE Latin1_General_100_BIN2
(или что-то подобное).И может ли ценность
[CODE]
когда-либо измениться? Если да, то это еще одна причина не использовать его в качестве PK (даже если вы установили FK наON UPDATE CASCADE
). Если он не может или никогда не изменится, это нормально, но все же есть более чем достаточно причин, чтобы не использовать его в качестве кластерного ПК.Конечно, вопрос может быть неправильно сформулирован, поскольку кажется, что у вас уже есть это поле в вашем ПК.
Независимо от этого, наилучшим вариантом, безусловно, является использование
[ID_CODE]
в качестве кластерного PK, использование этого поля в связанных таблицах как FK и сохранение его[CODE]
какUNIQUE INDEX
(что означает, что это «альтернативный ключ»).Обновление
Немного больше информации на основе этого вопроса в комментарии к этому ответу:
Все это зависит от множества факторов, некоторые из которых я уже упоминал, но повторю:
Первичный ключ - это способ идентификации отдельной строки независимо от того, на нее ссылаются какие-либо внешние ключи. То, как ваша система внутренне идентифицирует строку, связано, но не обязательно, с тем, как ваши пользователи идентифицируют себя / эту строку. Любой столбец NOT NULL с уникальными данными может работать, но есть вопросы практичности, которые следует учитывать, особенно если на PK на самом деле ссылаются какие-либо FK. Например, GUID являются уникальными, и некоторым людям действительно нравится использовать их по разным причинам, но они довольно плохи для кластерных индексов (
NEWSEQUENTIALID
лучше, но не идеально). С другой стороны, GUID просто хороши как альтернативные ключи и используются приложением для поиска строки, но JOIN все еще выполняется с использованием INT (или подобного) PK.До сих пор вы не сказали нам, как
[CODE]
поле вписывается в систему со всех сторон, за исключением упоминания о том, что именно так вы просматриваете строки, но это для всех запросов или только для некоторых? Следовательно:Что касается
[CODE]
значения:Что касается этой таблицы:
[CODE]
или[ID_CODE]
) используются в других таблицах, даже если явно не с внешним ключом?[CODE]
единственное поле используется для получения отдельных строк, то для чего оно предназначено[ID_CODE]
? Если он не используется, зачем его вообще (что может зависеть от ответа «Может ли[CODE]
поле когда-нибудь измениться?»)?Это решение не может быть принято исключительно по вопросу «NVARCHAR да или нет?». Я снова скажу, что, вообще говоря, я не считаю, что это хорошая идея, но бывают случаи, когда это хорошо. Учитывая, что в этой таблице так мало полей, маловероятно, что индексов будет больше или, по крайней мере, не так много. Таким образом, вы могли бы быть хорошо в любом случае иметь
[CODE]
в качестве кластерного индекса. И если никакие другие таблицы не ссылаются на эту таблицу, то вы также можете сделать ее PK. Но если другие таблицы ссылаются на эту таблицу, я бы выбрал[ID_CODE]
поле в качестве PK, даже если не кластеризован.источник
[ID_CODE]
, какPRIMARY KEY
лучше всего, если я использую[CODE]
столбец для поиска в таблице?Вы должны разделить понятия:
Первичный ключ - это концепция проекта , логическое свойство записей в таблице. Он должен быть неизменным в течение времени жизни записи таблицы и должен быть ключом, используемым в приложении для ссылки на запись.
Кластерный индекс - это концепция хранения , физическое свойство. Это должен быть самый распространенный путь доступа к запросам, он должен удовлетворять в качестве покрывающего индекса для большинства случаев и удовлетворять как можно большему количеству запросов диапазона.
Не требуется, чтобы первичный ключ был кластеризованным индексом. Вы можете иметь
ID_CODE
как PK, так и(CODE_LEVEL, CODE)
кластерный ключ. Или наоборот.Больший кластеризованный ключ имеет некоторые отрицательные последствия, так как более широкий ключ означает более низкую плотность на страницах индекса и больший размер, используемый для всех некластеризованных индексов. по этой теме уже пролито тонны чернил, например. Начнем с Дополнительные соображения относительно ключа кластеризации - дебаты по кластерному индексу продолжаются! ,
Но суть дела в том, что выбор ключа кластерного индекса - это прежде всего компромисс. С одной стороны, у вас есть требования к размеру хранилища с общими последствиями для производительности (больший ключ -> больший размер -> больше ввода-вывода, и пропускная способность ввода-вывода, вероятно, является самым дефицитным ресурсом, который у вас есть). С другой стороны, выбор неправильного кластеризованного ключа в качестве экономии пространства может иметь последствия для производительности запросов, часто хуже, чем проблемы, вызванные широким ключом.
Что касается выбора первичного ключа, это даже не должно быть проблемой: ваша модель данных, логика вашего приложения должны определять первичный ключ.
Это , как говорится, мой 2с:
NVARCHAR(20)
это не широкий. Это вполне приемлемый размер кластеризованного ключа, даже для большого стола.источник
[ID_CODE]
, какPRIMARY KEY
лучше всего, если я использую[CODE]
столбец (и, возможно,[CODE_LEVEL]
), чтобы просмотреть таблицу?[CODE]
колонку в качестве первичного ключа.Я бы никогда не позволил кому-либо сделать
nvarchar(20)
PK в моей базе данных. Вы тратите место на диске и кеш-память. Каждый индекс в этой таблице и все FK в ней копируют это широкое значение. Может быть, символ (20), если они могут это оправдать. Какие данные вы пытаетесь сохранитьCODE
? Вам действительно нужно хранить символы nvarchar? Я склонен делать PK «внутренними» значениями, невидимыми для пользователей, и стараюсь хранить значения, которые отображаются отдельно. Отображаемые значения иногда нуждаются в изменении, что становится очень проблематичным для PK + FK.Кроме того, понимаете ли вы, что «идентификация bigint (1,1)» может увеличиваться до 9,223,372,036,854,775,807?
Если вы не создаете эту базу данных для Google, не будет ли нормальным
int identity (1,1)
с лимитом более 2 миллиардов?источник
[ID_CODE]
, какPRIMARY KEY
лучше всего, если я использую[CODE]
столбец для поиска в таблице? Спасибо.Не должно быть никакого врожденного / заметного наказания, за исключением того, что вы рискуете использовать широкие ключи при использовании nvarchar / varchar, если не знаете. Особенно, если вы начнете объединять их в составные ключи.
Но в вашем примере длины (20) у вас все будет хорошо, и я бы не стал сильно беспокоиться об этом. Потому что, если CODE - это то, как вы в основном запрашиваете свои данные - кластерный индекс по этому звучит очень разумно.
Однако вам следует подумать, действительно ли вы хотите использовать его в качестве первичного ключа или просто уникального (кластеризованного) индекса. Существует (небольшая) разница между кластеризованным индексом и первичным ключом (в основном - первичный ключ идентифицирует ваши данные, но индекс - это то, как вы запрашиваете данные), так что если вы хотите, вы могли бы так же легко сделать свой ID_Code как первичный ключ и сделать уникальный кластеризованный индекс над кодом. (обратите внимание: SQL Server автоматически превратит ваш первичный ключ в кластеризованный индекс, если вы сами не создали кластерный индекс)
Также подумайте, нужен ли вам ID_Code, теперь у вас есть уникальный код.
источник
NVARCHAR(20)
его размер составляет не более 40 байт, и, поскольку он является столбцом переменной длины , на самом деле это не лучший выбор для кластеризованного индекса.ID_CODE
будучиBIGINT IDENTITY
бы быть гораздо лучше , выбор здесь!