web-dev-qa-db-fra.com

Comment créer une véritable relation un à un dans SQL Server

Je souhaite créer une relation un-à-un dans SQL Server 2008 R2.

J'ai deux tables tableA et tableB, je règle la clé primaire de tableB comme clé étrangère qui fait référence à la primaire de tableA. Mais lorsque j'utilise tout d'abord la base de données Entity Framework, le modèle est compris entre 1 et 0..1.

Quelqu'un sait comment créer une vraie relation 1 à 1 dans la base de données?

Merci d'avance!

32
James

Je suis presque sûr qu'il est techniquement impossible dans SQL Server d'avoir une relation vraie 1 à 1, car cela signifierait que vous auriez devez insérer les deux enregistrements en même temps (sinon, vous obtiendriez une erreur de contrainte insert), dans les deux tables, les deux ayant une relation de clé étrangère entre elles.

Cela dit, la conception de votre base de données décrite avec une clé étrangère est une relation de 1 à 0..1. Il n'y a pas de contrainte possible qui nécessiterait un enregistrement dans la tableB. Vous pouvez avoir une pseudo-relation avec un déclencheur qui crée l'enregistrement dans la tableB. 

Donc, il y a quelques pseudo-solutions

Commencez par stocker toutes les données dans une seule table. Ensuite, vous n'aurez aucun problème avec EF.

Ou, deuxièmement, votre entité doit être suffisamment intelligente pour ne pas autoriser une insertion sauf si elle est associée à un enregistrement.

Ou, troisièmement, et probablement, vous avez un problème que vous essayez de résoudre et vous nous demandez pourquoi votre solution ne fonctionne pas au lieu du problème réel que vous essayez de résoudre (un problème XY) .

METTRE À JOUR

Pour expliquer dans RÉALITÉ comment les relations 1 à 1 ne fonctionnent pas, je vais utiliser l'analogie du dilemme poulet ou œuf . Je n'ai pas l'intention de résoudre ce dilemme, mais si vous aviez une contrainte qui dit que pour ajouter un oeuf à la table des oeufs, la relation entre le poulet et le poulet doit exister, et le poulet doit exister dans le tableau, vous ne pouviez pas ajouter un oeuf à la table des oeufs. L'inverse est également vrai. Vous ne pouvez pas ajouter de poulet à la table Poulet sans la relation entre l'œuf et l'œuf existant dans la table Œuf. Ainsi, aucun enregistrement ne peut être entièrement créé dans une base de données sans enfreindre l'une des règles/contraintes.

La base de données nomenclature d'une relation un-à-un est trompeuse. Toutes les relations que j'ai vues (donc mon expérience) seraient plus descriptives que des relations de un à un (zéro ou un).

45
Erik Philips

Définissez la clé étrangère en tant que clé primaire, puis définissez la relation entre les deux champs de clé primaire. C'est tout! Vous devriez voir un signe de clé aux deux extrémités de la ligne de relation. Cela représente un un à un. 

enter image description here

Vérifiez ceci: Conception d'une base de données SQL Server avec une relation un à un

55
Pranay Rana

Pour ce faire, créez une relation de clé étrangère primaire simple et définissez la colonne de clé étrangère sur unique de la manière suivante:

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);

Vérifiez si tout va bien

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

Maintenant, généralement dans une relation étrangère primaire (un à plusieurs), Vous pouvez entrer plusieurs fois EmployeeID, Mais ici une erreur sera renvoyée

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

L’instruction ci-dessus montrera l’erreur comme

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

Je sais comment établir une relation strictement * un à un sans utiliser de déclencheurs, de colonnes calculées, de tables supplémentaires ou d’autres astuces «exotiques» (uniquement des clés étrangères et des contraintes uniques), avec une petite mise en garde.

J'emprunterai le concept poulet-et-l'oeuf de la réponse acceptée pour m'aider à expliquer la mise en garde.

Il est un fait qu'un poulet ou un œuf doit venir en premier (dans les bases de données actuelles de toute façon). Heureusement, cette solution n’est pas politique et ne spécifie pas ce qui doit venir en premier, elle la laisse à l’applicateur.

La mise en garde est que la table qui permet techniquement à un enregistrement de «venir en premier» peut permettre la création d'un enregistrement sans l'enregistrement correspondant dans l'autre table; toutefois, dans cette solution, un seul enregistrement de ce type est autorisé. Lorsqu'un seul enregistrement est créé (uniquement poulet ou œuf), aucun autre enregistrement ne peut être ajouté à l'une des deux tables tant que l'enregistrement «solitaire» n'est pas supprimé ou qu'un enregistrement correspondant n'est créé dans l'autre table.

Solution:

Ajoutez des clés étrangères à chaque table en référençant l’autre, ajoutez des contraintes uniques à chaque clé étrangère et rendez une clé étrangère nullable, l’autre non nullable et une clé primaire. Pour que cela fonctionne, la contrainte unique sur la colonne nullable ne doit autoriser qu'une seule valeur NULL (c'est le cas dans SQL Server, ce qui n'est pas sûr pour les autres bases de données).

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

Pour insérer, il faut d’abord insérer un oeuf (avec zéro pour le poulet). Maintenant, seul un poulet peut être inséré et il doit faire référence à l'œuf "non réclamé". Enfin, l'oeuf ajouté peut être mis à jour et il doit faire référence au poulet "non réclamé". À aucun moment, deux poulets ne peuvent faire référence au même œuf ou inversement.

Pour supprimer, la même logique peut être suivie: mettez à jour le poulet de l'œuf en null, supprimez le poulet nouvellement «non réclamé», supprimez l'œuf.

Cette solution permet également d’échanger facilement. Il est intéressant de noter que la permutation pourrait être l’argument le plus puissant en faveur de l’utilisation d’une telle solution, car elle présente un potentiel d’utilisation pratique. Normalement, dans la plupart des cas, il est préférable de mettre en place une relation un à un entre deux tables en reformulant simplement les deux tables en une seule; Cependant, dans un scénario potentiel, les deux tableaux peuvent représenter des entités véritablement distinctes, qui nécessitent une relation strictement individuelle, mais qui doivent souvent être échangées entre des "partenaires" ou être réorganisées en général, tout en conservant les relations individuelles. -une relation après le réarrangement. Si la solution la plus courante était utilisée, toutes les colonnes de données de l'une des entités devraient être mises à jour/écrasées pour toutes les paires en cours de réorganisation, par opposition à cette solution dans laquelle une seule colonne de clés étrangères doit être réorganisée. (la colonne de clé étrangère nullable).

Eh bien, c’est le mieux que je puisse faire en utilisant des contraintes standard (ne jugez pas :) peut-être que quelqu'un le trouvera utile.

4
tomosius

1 à 1 Les relations en SQL sont établies en fusionnant le champ des deux tableaux en un!

Je sais que vous pouvez diviser une table en deux entités avec une relation de 1 à 1. La plupart du temps, vous utilisez ceci parce que vous souhaitez utiliser le chargement paresseux sur "un champ lourd de données binaires dans une table". 

Exemple: Vous avez une table contenant des images avec une colonne de nom (chaîne), peut-être une colonne de métadonnées, une colonne de vignette et l'image elle-même varbinary (max). Dans votre application, vous n'aurez certainement à afficher que le nom et la vignette d'un contrôle de collection, puis à charger les "données d'image complètes" uniquement si nécessaire.

Si c'est ce que vous recherchez. C'est quelque chose appelé "division de table" ou "division horizontale".

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

2
Marco Guignard

Le moyen le plus simple d'y parvenir est de créer une seule table avec les champs Table A et B, PAS NULL. De cette façon, il est impossible d'avoir l'un sans l'autre. 

0
Jonathan Nappee