2011-05-26 1 views
1

Je travaille dans SQLAzure pour le moment.Vérification que les éléments ne sont pas liés de manière incorrecte - est-ce que c'est pour cela que les "contraintes de clés étrangères" sont?

J'installe une conception où chaque utilisateur dispose d'un certain nombre de son adresse

Lorsque l'utilisateur place ensuite un ordre, je veux lier cette commande à l'utilisateur et à un couple d'adresses.

Alors mes tableaux ressemblent:

utilisateur

  • Id
  • Nom
  • etc

Adresse

  • Id
  • UserId (clé étrangère)
  • rue
  • etc

Commander

  • Id
  • UserId (clé étrangère)
  • DeliveryAddressId (clé étrangère)
  • BillingAddressId (clé étrangère)
  • etc

Est-il possible que je puis-je mettre en place un chèque dans SQL Server de sorte qu'un utilisateur ne peut en aucun cas (par exemple en piratant un HTML POST) soumettez une commande avec un AddressId qui n'est pas lié au même que l'UserId soumis. J'ai regardé les "contraintes de clés étrangères" dans les docs, mais cela ne semble pas être ce que je cherche.

Toutes les suggestions de quoi essayer - ou ce que les tutoriels à lire - seraient les plus appréciées.

+0

Vous devez vérifier toutes les données envoyées avant de l'insérer. Vous pouvez définir un champ à insérer dans votre structure ddbb, mais vous devez toujours vérifier le serveur de données avant d'atteindre le ddbb. – Chumillas

+0

Merci - je peux et je le ferai - mais je voudrais quand même mettre en place une vérification dans la base de données si je peux - comme la DB est en fin de compte où «l'argent s'arrête» quand il s'agit d'intégrité référentielle. – Stuart

+0

La seule façon de vérifier cette condition dans la base de données est d'avoir un déclencheur ON INSERT dans les tables en respectant l'intégrité. [HowTo] (http://msdn.microsoft.com/fr-fr/library/ee336242.aspx) – Chumillas

Répondre

3

En plus de votre clé primaire dans la table Address (sur Id), vous devez également déclarer une autre contrainte de clé, une contrainte UNIQUE, sur (Id, UserId).

ALTER TABLE Address ADD CONSTRAINT UQ_Address_UserCheck UNIQUE (Id,UserID) 

Vous pouvez remplacer vos FKs existants de commande à l'adresse, ou ajouter d'autres, qui vérifient les deux colonnes

ALTER TABLE Order ADD CONSTRAINT 
    FK_Order_DeliveryAddress_UserCheck FOREIGN KEY (DeliveryAddressID,UserID) 
    references Address (Id,UserId) 

Comme je le dis, vous pouvez ajouter ces tout comme des contraintes supplémentaires, si tu veux.


Alors, avec quelques légers coups secs de nommage, vos tableaux sont rendus comme ceci:

create table Users (
    UserID int IDENTITY(1,1) not null, 
    Name varchar(30) not null, 
    /* Other columns */ 
    constraint PK_Users PRIMARY KEY (UserID), 
    constraint UQ_User_Names UNIQUE (Name) 
) 
go 
create table Addresses (
    AddressID int IDENTITY(1,1) not null, 
    UserID int not null, 
    Street varchar(35) not null, 
    /* Other columns */ 
    constraint PK_Addresses PRIMARY KEY (AddressID), 
    constraint FK_Addresses_Users FOREIGN KEY (UserID) references Users (UserID), 
    constraint UQ_Addresses_UserCheck UNIQUE (UserID,AddressID) 
) 
go 
create table Orders (
    OrderID int IDENTITY (1,1) not null, 
    UserID int not null, 
    DeliveryAddressID int not null, 
    BillingAddressID int not null, 
    /* Other columns - there may be other nullability concerns above */ 
    constraint PK_Orders PRIMARY KEY (OrderID), 
    constraint FK_Orders_Users FOREIGN KEY (UserID) references Users (UserID), 
    constraint FK_Orders_DeliveryAddresses FOREIGN KEY (DeliveryAddressID) references Addresses (AddressID), 
    constraint FK_Orders_BillingAddresses FOREIGN KEY (BillingAddressID) references Addresses (AddressID), 
    /* Further constraints - ensure UserID -> AddressID match */ 
    constraint FK_Orders_DeliveryAddress_UserCheck FOREIGN KEY (UserID,DeliveryAddressID) references Addresses (UserID,AddressID), 
    constraint FK_Orders_BillingAddress_UserCheck FOREIGN KEY (UserID,BillingAddressID) references Addresses (UserID,AddressID) 
) 

Et essayer notre avec des inserts qui devraient fonctionner, sauf pour le dernier (où il y a un utilisateur/décalage adresse), il fonctionne:

declare @UID1 int 
declare @UID2 int 
declare @AID1_1 int 
declare @AID1_2 int 
declare @AID2_1 int 
declare @AID2_2 int 
insert into Users (Name) 
select 'User1' 
set @UID1 = SCOPE_IDENTITY() 
insert into Users (Name) 
select 'User2' 
set @UID2 = SCOPE_IDENTITY() 

insert into Addresses (UserID,Street) 
select @UID1,'Street1' 
set @AID1_1 = SCOPE_IDENTITY() 
insert into Addresses (UserID,Street) 
select @UID1,'Street2' 
set @AID1_2 = SCOPE_IDENTITY() 
insert into Addresses (UserID,Street) 
select @UID2,'Street1' 
set @AID2_1 = SCOPE_IDENTITY() 
insert into Addresses (UserID,Street) 
select @UID2,'Street2' 
set @AID2_2 = SCOPE_IDENTITY() 

insert into Orders (UserID,DeliveryAddressID,BillingAddressID) 
select @UID1,@AID1_1,@AID1_2 union all 
select @UID2,@AID2_1,@AID2_1 

insert into Orders (UserID,DeliveryAddressID,BillingAddressID) 
select @UID1,@AID1_1,@AID2_1 

Résultats:

(1 row(s) affected) 

(1 row(s) affected) 

(1 row(s) affected) 

(1 row(s) affected) 

(1 row(s) affected) 

(1 row(s) affected) 

(2 row(s) affected) 
Msg 547, Level 16, State 0, Line 31 
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Orders_BillingAddress_UserCheck". The conflict occurred in database "Test", table "dbo.Addresses". 
The statement has been terminated. 
+0

+1 pour un échantillon entièrement travaillé et entièrement fonctionnel - merci pour l'énorme effort – Stuart

1

créer un déclencheur ON INSERT pour effectuer toute la logique supplémentaire que vous souhaitez appliquer. L'inconvénient sera que l'utilisateur obtiendra un message d'erreur lorsqu'il essaie avec une mauvaise adresse ... pour être proactif, vous devriez le faire aussi dans l'interface graphique.

+0

+1 pour la vérification de l'interface graphique - ils sont tous les deux nécessaires, de la même manière que le côté client et le contrôle côté serveur sont utilisés pour les applications Web - l'un est d'accélérer la boucle de rétroaction pour le client, l'autre est de * garantir * l'exactitude. Mais -1 pour avoir recours aux déclencheurs. –

1

Est-ce qu'une clé étrangère composite de Order (UserID, DeliveryAddressID) à Address (UserID, ID) le ferait? (De même pour BillingAddressID)

+0

J'ai raté votre réponse quand j'ai posté la mienne - j'ai développé cette idée. –