web-dev-qa-db-ja.com

SQL Serverで実際の1対1の関係を作成する方法

SQL Server 2008 R2で1対1の関係を作成したい。

2つのテーブルtableAtableBがあり、tableBのプライマリキーを、tableAのプライマリを参照する外部キーとして設定します。しかし、Entity Frameworkデータベースを最初に使用する場合、モデルは1対0.1です。

データベースで実際の1対1の関係を作成する方法を知っている人はいますか?

39
James

SQL Serverでは、真の1対1の関係を持つことは技術的に不可能であると確信しています。つまり、両方のレコードを挿入するhave同時に(そうでない場合は挿入時に制約エラーが発生します)、両方のテーブルで、両方のテーブルが互いに外部キー関係を持っています。

つまり、外部キーで記述されたデータベース設計は1対0..1の関係です。 tableBのレコードを必要とする制約はありません。 tableBにレコードを作成するトリガーとの疑似関係を持つことができます。

そのため、いくつかの疑似ソリューションがあります

まず、すべてのデータを単一のテーブルに保存します。その後、EFで問題は発生しません。

または、第2に、エンティティは、関連付けられたレコードがない限り挿入を許可しないように十分にスマートでなければなりません。

または、第三に、そして最も可能性の高い、 解決しようとしている問題があり、解決しようとしている実際の問題(XY問題)の代わりにソリューションが機能しない理由を尋ねています =。

[〜#〜] update [〜#〜]

[〜#〜] reality [〜#〜]で1対1のリレーションシップが機能しない方法を説明するには、 Chickenまたは卵のジレンマ 。私はこのジレンマを解決するつもりはありませんが、卵を卵テーブルに追加するための制約がある場合、鶏の関係が存在し、鶏がテーブルに存在する必要があります。 EggテーブルにEggを追加できませんでした。その逆も同様です。鶏肉と鶏肉の両方が鶏卵テーブルに存在しない限り、鶏肉を鶏肉テーブルに追加することはできません。したがって、ルール/制約のいずれかを破ることなく、データベース内にすべてのレコードを作成することはできません。

1対1の関係のデータベース命名法は誤解を招きます。私が見たすべての関係(したがって私の経験)は、1対(ゼロまたは1)の関係としてより記述的です。

53
Erik Philips

外部キーを主キーとして設定し、両方の主キーフィールドに関係を設定します。それでおしまい!関係線の両端にキー記号が表示されます。これは1対1を表します。

enter image description here

これを確認してください: 1対1の関係を持つSQL Serverデータベース設計

59
Pranay Rana

これは、単純な主外部キー関係を作成し、次の方法で外部キー列を一意に設定することで実行できます。

CREATE TABLE [Employee] (
    [ID]    INT PRIMARY KEY
,   [Name]  VARCHAR(50)
);

CREATE TABLE [Salary] (
    [EmployeeID]    INT UNIQUE NOT NULL
,   [SalaryAmount]  INT 
);

ALTER TABLE [Salary]
ADD CONSTRAINT FK_Salary_Employee FOREIGN KEY([EmployeeID]) 
    REFERENCES [Employee]([ID]);

Schema

INSERT INTO [Employee] (
    [ID]
,   [Name]
)
VALUES
    (1, 'Ram')
,   (2, 'Rahim')
,   (3, 'Pankaj')
,   (4, 'Mohan');

INSERT INTO [Salary] (
    [EmployeeID]
,   [SalaryAmount]
)
VALUES
    (1, 2000)
,   (2, 3000)
,   (3, 2500)
,   (4, 3000);

すべてが正常かどうかを確認します

SELECT * FROM [Employee];
SELECT * FROM [Salary];

現在、主な外部関係(1対多)では、EmployeeIDを複数回入力できますが、ここではエラーがスローされます

INSERT INTO [Salary] (
    [EmployeeID]
,   [SalaryAmount]
)
VALUES
    (1, 3000);

上記のステートメントは、エラーを次のように表示します

    Violation of UNIQUE KEY constraint 'UQ__Salary__7AD04FF0C044141D'. 
    Cannot insert duplicate key in object 'dbo.Salary'. The duplicate key value is (1).
23
ashim

トリガー、計算列、追加のテーブル、またはその他の「エキゾチック」なトリック(外部キーと一意の制約のみ)を使用せずに、厳密に1対1の関係を実現する方法を1つだけ知っています。

警告の説明に役立つように、受け入れられた回答から鶏と卵の概念を借ります。

鶏肉または卵のいずれかが最初に来なければならないという事実です(とにかく現在のDBでは)。幸いなことに、この解決策は政治的になることはなく、どちらを優先するかを規定していません-それは実装者に任されています。

注意点は、レコードが技術的に「最初に」来ることを許可するテーブルは、他のテーブルに対応するレコードなしでレコードを作成できることです。ただし、このソリューションでは、そのようなレコードは1つだけ許可されます。 1つのレコードのみが作成される場合(鶏肉または卵のみ)、「孤独な」レコードが削除されるか、一致するレコードが他のテーブルに作成されるまで、2つのテーブルのいずれにもレコードを追加できません。

解決:

各テーブルに外部キーを追加し、他を参照し、各外部キーに一意の制約を追加し、一方の外部キーをNULL可能、もう一方をNULL不可、およびプライマリキーにします。これが機能するには、null許容列の一意の制約で1つのnullのみを許可する必要があります(これはSQL Serverの場合であり、他のデータベースについては不明です)。

CREATE TABLE dbo.Egg (
    ID int identity(1,1) not null,
    Chicken int null,
    CONSTRAINT [PK_Egg] PRIMARY KEY CLUSTERED ([ID] ASC) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE dbo.Chicken (
    Egg int not null,
    CONSTRAINT [PK_Chicken] PRIMARY KEY CLUSTERED ([Egg] ASC) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE dbo.Egg  WITH NOCHECK ADD  CONSTRAINT [FK_Egg_Chicken] FOREIGN KEY([Chicken]) REFERENCES [dbo].[Chicken] ([Egg])
GO
ALTER TABLE dbo.Chicken  WITH NOCHECK ADD  CONSTRAINT [FK_Chicken_Egg] FOREIGN KEY([Egg]) REFERENCES [dbo].[Egg] ([ID])
GO
ALTER TABLE dbo.Egg WITH NOCHECK ADD CONSTRAINT [UQ_Egg_Chicken] UNIQUE([Chicken])
GO
ALTER TABLE dbo.Chicken WITH NOCHECK ADD CONSTRAINT [UQ_Chicken_Egg] UNIQUE([Egg])
GO

挿入するには、まずEggを挿入する必要があります(チキンの場合はnull)。現在、挿入できるのは鶏肉のみで、「未請求の」卵を参照する必要があります。最後に、追加されたEggは更新可能で、「未請求の」鶏肉を参照する必要があります。 2つの鶏が同じ卵を参照したり、その逆を参照したりすることはできません。

削除するには、同じロジックに従います:Egg's Chickenをnullに更新し、新しく「要求されていない」チキンを削除し、Eggを削除します。

このソリューションでは、簡単に交換することもできます。興味深いことに、スワップは潜在的な実用性があるため、そのようなソリューションを使用するための最も強力な議論かもしれません。通常、ほとんどの場合、2つのテーブルを1つにリファクタリングするだけで、2つのテーブルの1対1の関係をより適切に実装できます。ただし、潜在的なシナリオでは、2つのテーブルは厳密に1対1の関係を必要とする真に別個のエンティティを表すことがありますが、「パートナー」を頻繁に交換するか、一般的には1対1 -再配置後の1つの関係。より一般的なソリューションが使用された場合、外部キーの1列のみを再配置する必要があるこのソリューションとは対照的に、再配置されるすべてのペアに対して1つのエンティティのすべてのデータ列を更新/上書きする必要があります(null許容の外部キー列)。

まあ、これは標準的な制約を使用して行うことができる最善の方法です(判断しないでください)。

4
tomosius

SQLの1対1の関係は、両方のテーブルのフィールドを1つにマージすることで作成されます。

テーブルを1対1の関係を持つ2つのエンティティに分割できることを知っています。ほとんどの場合、「テーブル内のバイナリデータの重いフィールド」で遅延読み込みを使用するため、これを使用します。

例:名前列(文字列)、いくつかのメタデータ列、サムネイル列、および画像自体varbinary(max)を含む画像を含むテーブルがあります。アプリケーションでは、コレクションコントロールの名前とサムネイルのみを最初に表示し、必要な場合にのみ「フルピクチャデータ」をロードします。

あなたが探しているものなら。これは、「テーブル分割」または「水平分割」と呼ばれるものです。

https://visualstudiomagazine.com/articles/2014/09/01/splitting-tables.aspx

1
Marco Guignard

これを実現する最も簡単な方法は、テーブルAとBの両方のフィールドがNULLでないテーブルを1つだけ作成することです。このように、一方を他方なしで使用することは不可能です。

0
Jonathan Nappee

これはどうですか ?

create table dbo.[Address]
(
Id int identity not null,
City nvarchar(255) not null,
Street nvarchar(255) not null,
CONSTRAINT PK_Address PRIMARY KEY (Id)
)

create table dbo.[Person]
(
Id int identity not null,
AddressId int not null,
FirstName nvarchar(255) not null,
LastName nvarchar(255) not null,
CONSTRAINT PK_Person PRIMARY KEY (Id),
CONSTRAINT FK_Person_Address FOREIGN KEY (AddressId) REFERENCES dbo.[Address] (Id)
)
0
Muflix