Внешний ключ для первичного ключа



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



CREATE TABLE table1
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AnotherID INT NOT NULL,
SomeData VARCHAR(100) NOT NULL
)

CREATE TABLE table2
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AnotherID INT NOT NULL,
MoreData VARCHAR(30) NOT NULL,

CONSTRAINT fk_table2_table1 FOREIGN KEY (AnotherID) REFERENCES table1 (AnotherID)
)


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

475   3  

3 ответов:

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

С Книги Онлайн:

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

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

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

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

рассмотрим случай таблицы Customer со столбцом SSN (и немым первичным ключом) и таблицей утверждений, которая также содержит столбец SSN (заполненный бизнес-логикой из данных клиента, но ФК не существует). Конструкция имеет недостатки, но используется в течение нескольких лет, и три различных приложения были построены на схеме. Должно быть очевидно, что вырывается претензия.SSN и введение реальных отношений PK-FK было бы идеальным, но также было бы значительное капитальный ремонт. С другой стороны, наложение уникального ограничения на клиента.SSN, и добавление FK по требованию.ССН, смогло обеспечить ссылочную целостность, с небольшим или никаким ударом на приложения.

Не поймите меня неправильно, я все за нормализацию, но иногда прагматизм побеждает идеализм. Если посредственный дизайн можно помочь с пластырем, операции можно избежать.

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

Проблема в том, что если у вас есть эта проблема, схема базы данных денормализована.

вы, например, храните комнаты в таблице с первичным ключом room-uid, полем DateFrom и DateTo и другим uid, здесь RM_ApertureID для отслеживания той же комнаты и поля мягкого удаления, например RM_Status, где 99 означает "удалено", а 99 означает "активно".

поэтому при создании первой комнаты вы вставляете RM_UID и RM_ApertureID как то же значение, что и RM_UID. Затем, когда вы завершаете комнату до даты и восстанавливаете ее с новым диапазоном дат, RM_UID является newid(), а RM_ApertureID из предыдущей записи становится новым RM_ApertureID.

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

и там невозможно установить внешний ключ для неуникального столбца / индекса, например, в T_ZO_REM_AP_Raum_Reinigung (где RM_UID фактически является RM_ApertureID).
Но чтобы запретить недопустимые значения, нужно установить внешний ключ, иначе data-garbage будет результатом раньше, чем позже...

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

IF  EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]'))
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung DROP CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
GO


IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fu_Constaint_ValidRmApertureId]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fu_Constaint_ValidRmApertureId]
GO




CREATE FUNCTION [dbo].[fu_Constaint_ValidRmApertureId](
     @in_RM_ApertureID uniqueidentifier 
    ,@in_DatumVon AS datetime 
    ,@in_DatumBis AS datetime 
    ,@in_Status AS integer 
) 
    RETURNS bit 
AS 
BEGIN   
    DECLARE @bNoCheckForThisCustomer AS bit 
    DECLARE @bIsInvalidValue AS bit 
    SET @bNoCheckForThisCustomer = 'false' 
    SET @bIsInvalidValue = 'false' 

    IF @in_Status = 99 
        RETURN 'false' 


    IF @in_DatumVon > @in_DatumBis 
    BEGIN 
        RETURN 'true' 
    END 


    IF @bNoCheckForThisCustomer = 'true'
        RETURN @bIsInvalidValue 


    IF NOT EXISTS
    ( 
        SELECT 
             T_Raum.RM_UID 
            ,T_Raum.RM_Status 
            ,T_Raum.RM_DatumVon 
            ,T_Raum.RM_DatumBis 
            ,T_Raum.RM_ApertureID 
        FROM T_Raum 
        WHERE (1=1) 
        AND T_Raum.RM_ApertureID = @in_RM_ApertureID 
        AND @in_DatumVon >= T_Raum.RM_DatumVon 
        AND @in_DatumBis <= T_Raum.RM_DatumBis 
        AND T_Raum.RM_Status <> 99  
    ) 
        SET @bIsInvalidValue = 'true' -- IF ! 

    RETURN @bIsInvalidValue 
END 



GO



IF  EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]'))
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung DROP CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
GO


-- ALTER TABLE dbo.T_AP_Kontakte WITH CHECK ADD CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]  
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung WITH NOCHECK ADD CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] 
CHECK 
( 
    NOT 
    ( 
        dbo.fu_Constaint_ValidRmApertureId(ZO_RMREM_RM_UID, ZO_RMREM_GueltigVon, ZO_RMREM_GueltigBis, ZO_RMREM_Status) = 1 
    ) 
) 
GO


IF  EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]')) 
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung CHECK CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] 
GO

Comments

    Ничего не найдено.