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


15

Я розробляю базу даних SQL Server 2012, і у мене виникає питання про відношення "один до нуля" або "один".

У мене дві таблиці, Codesі HelperCodes. Код може мати нуль або один допоміжний код. Це сценарій sql для створення цих двох таблиць та їх зв’язків:

CREATE TABLE [dbo].[Code]
(
    [Id] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [SentToRanger] BIT NOT NULL DEFAULT 0, 
    [LastChange] NVARCHAR(50) NOT NULL, 
    [UserName] NVARCHAR(50) NOT NULL, 
    [Source] NVARCHAR(50) NOT NULL, 
    [Reason] NVARCHAR(200) NULL, 
    [HelperCodeId] NVARCHAR(20) NULL,
    CONSTRAINT [PK_Code] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ),
    CONSTRAINT [FK_Code_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level]),
    CONSTRAINT [FK_Code_HelperCode]
       FOREIGN KEY ([HelperCodeId])
        REFERENCES [dbo].[HelperCode] ([HelperCodeId])
)

CREATE TABLE [dbo].[HelperCode]
(
    [HelperCodeId] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [LastChange] NVARCHAR(50) NOT NULL,
    CONSTRAINT [PK_HelperCode] PRIMARY KEY CLUSTERED
    (
        [HelperCodeId] ASC
    ),
    CONSTRAINT [FK_HelperCode_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level])
)

Це правильно?

Код і HelperCode - це обидві сутності. HelperCode може бути використаний (жоден Кодекс не посилається на нього) або використаний (лише один Кодекс посилається на нього).

Можливо Code.HelperCodeId повинен бути частиною первинного ключа таблиці таблиці Code. Але я не впевнений, чи може нульовий стовпець бути частиною первинного. Роблячи це, я хочу запобігти тому, щоб два чи більше кодів посилалися на той самий HelperCode.


1
Чому ви хочете HelperCodeIdбути частиною ПК? Чи це випадково, тому що ви хочете не допустити, щоб два чи більше кодів посилалися на той самий HelperCode?
Андрій М

Так, я хочу запобігти тому, щоб два чи більше кодів посилалися на той самий HelperCode. Інший варіант - встановити HelperCodeIdстовпчик як унікальний.
VansFannel

@ypercube Чи можете ви, будь ласка, додати повне речення sql як відповідь? Я не дуже часто працюю з sql і не знаю, як це зробити. Спасибі.
VansFannel

Концептуально інженери СУБД не могли дозволити NULL в первинних ключах, не суперечивши всій реляційній моделі даних. І реляційна модель є частиною того, що робить реляційні бази даних настільки корисними. Ви можете або не зацікавлені в цьому аспекті, але це важливо вказати на майбутніх відвідувачів.
Вальтер Мітті

@WalterMitty Я ніколи не розумів, чому наявність нульового значення в ПК допоможе знищити значення, яке приносить RDBMS. Я чув це багато разів. Чи можете ви докладно?
usr

Відповіді:


24

Щоб відповісти на запитання в заголовку, ні, всі основні стовпці повинні бути NOT NULL.

Але не змінюючи дизайн таблиць, ви можете додати відфільтрований індекс у Code (HelperCodeId)стовпчик:

CREATE UNIQUE INDEX 
    FUX_Code_HelperCodeId
ON dbo.Code 
    (HelperCodeId) 
WHERE 
    HelperCodeId IS NOT NULL ;

Фільтр ( WHERE HelperCodeId IS NOT NULL) необхідний через те, як SQL-сервер обробляє нулі в унікальних обмеженнях та унікальних індексах. Без фільтра, SQL-сервер не дозволить більше одного рядка з NULLв HelperCodeId.


Альтернативна конструкція буде видалити HelperCodeIdз Codeі додати третю таблицю , в якій буде зберігатися Code- HelperCodeвідносини. Зв'язок між двома сутностями здається нульовим або один-до-нульовим-або-одним (в обох кодах не може бути HelperCode, а HelperCode не може використовуватися ніяким кодом):

CREATE TABLE [dbo].[Code]
(
    [Id] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [SentToRanger] BIT NOT NULL DEFAULT 0, 
    [LastChange] NVARCHAR(50) NOT NULL, 
    [UserName] NVARCHAR(50) NOT NULL, 
    [Source] NVARCHAR(50) NOT NULL, 
    [Reason] NVARCHAR(200) NULL, 
    -- 
    -- removed:   [HelperCodeId] NVARCHAR(20) NULL,
    -- 
    CONSTRAINT [PK_Code] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ),
    CONSTRAINT [FK_Code_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level]),
) ;

HelperCode залишається незмінним:

CREATE TABLE [dbo].[HelperCode]
(
    [HelperCodeId] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [LastChange] NVARCHAR(50) NOT NULL,
    CONSTRAINT [PK_HelperCode] PRIMARY KEY CLUSTERED
    (
        [HelperCodeId] ASC
    ),
    CONSTRAINT [FK_HelperCode_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level])
) ;

Додаткова таблиця матиме два UNIQUEпротипоказання (або один основний і один унікальний), щоб гарантувати, що кожен Код пов'язаний з (максимум) одним HelperCode, а кожен HelperCode пов'язаний з (максимум) одним Кодом. Обидва стовпці будуть NOT NULL:

CREATE TABLE [dbo].[Code_HelperCode]
(
    [CodeId] NVARCHAR(20) NOT NULL, 
    [HelperCodeId] NVARCHAR(20) NOT NULL, 
    CONSTRAINT [UQ_Code_HelperCode_CodeId]
       UNIQUE (CodeId),
    CONSTRAINT [UQ_Code_HelperCode_HelperCodeId]
       UNIQUE (HelperCodeId),
    CONSTRAINT [FK_HelperCode_Code]
       FOREIGN KEY ([CodeId])
        REFERENCES [dbo].[Code] ([Id]),
    CONSTRAINT [FK_Code_HelperCode]
       FOREIGN KEY ([HelperCodeId])
        REFERENCES [dbo].[HelperCode] ([HelperCodeId])
) ;

Спасибі, ви можете змінити дизайн, якщо хочете. Я міг багато чого навчитися.
VansFannel

Дякуємо за ваш дизайн. Я не додав нову таблицю, тому що вважав, що ці таблиці використовуються лише у відносинах "багато до багатьох".
VansFannel

0

Спробуйте скористатися унікальним обмеженням. Нібито стандарт ANSI оголосив нулі первинним ключем недійсним, але я ніколи не бачив цього стандарту і не хочу купувати його, щоб перевірити це.

Немає нульових ключів, здається, одна з тих речей, в які розробники дуже твердо вірять у той чи інший спосіб. Я вважаю за краще використовувати їх, тому що мені здається корисним для пошуку таблиць, що містять підказки та пов’язані дані для комбінованих ящиків, які не заповнюються.

Мене вчили, що значення Null вказує на те, що змінна ніколи не була встановлена, а порожнє значення вказує на те, що значення було встановлено в минулому. Звичайно, розробник повинен визначитися з додатком, але я вважаю безглуздим дозволити пусті первинні ключі, але не нульові первинні ключі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.