Sql

非主キーへの外部キー



Foreign Key Non Primary Key



解決:

非主キーへの外部キーを本当に作成したい場合は、一意の制約がある列である必要があります。

Books Onlineから:



FOREIGN KEY制約は、別のテーブルのPRIMARYKEY制約にのみリンクする必要はありません。別のテーブルのUNIQUE制約の列を参照するように定義することもできます。

だからあなたの場合あなたが作るなら一意のAnotherID、それは許可されます。一意の制約を適用できない場合は運が悪いですが、それについて考えると、これは本当に理にかなっています。



前述のように、候補キーとして完全に優れた主キーがある場合は、それを使用してみませんか?


他の人が指摘しているように、理想的には、外部キーは主キー(通常はIDENTITY列)への参照として作成されます。ただし、私たちは理想的な世界に住んでおらず、スキーマへの「小さな」変更でさえ、アプリケーションロジックに重大な波及効果をもたらす可能性があります。

SSN列(およびダム主キー)を持つCustomerテーブルと、SSN列も含むClaimテーブル(Customerデータからビジネスロジックによって入力されますが、FKは存在しません)の場合を考えてみます。設計には欠陥がありますが、数年間使用されており、3つの異なるアプリケーションがスキーマ上に構築されています。 Claim.SSNを取り除いて、実際のP​​K-FK関係を設定することが理想的であるのは明らかですが、 重要 オーバーホール。一方、Customer.SSNにUNIQUE制約を設定し、Claim.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である場合)。
ただし、無効な値を禁止するには、外部キーを設定する必要があります。そうしないと、データガベージが後でではなく早く結果になります...

この場合(アプリケーション全体を書き直すことを除いて)できることは、キーの存在をチェックするスカラー関数を使用して、CHECK制約を挿入することです。

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_IP(N '[dbo]。[T_ZO_REM_AP_Raum 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' '、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 BEGIN DECLARE @bNoCheckForThisCustomerASビットDECLARE @ bIsInvalidValueASビットSET @ bNoCheckForThisCustomer = 'false' SET @bIsInvalidValue = 'false' IF @in_Status = 99 RETURN'false 'IF @in_DatumVon> @in_DatumBis BEGIN RETURN'true' END IF @bNoCheckFor 'NS '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 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