Проверка того, что элементы не связаны неправильно - для этого предназначены «Ограничения внешнего ключа»? - PullRequest
1 голос
/ 26 мая 2011

Я сейчас работаю в SQLAzure.

Я настраиваю дизайн, в котором у каждого пользователя есть номер адреса

Когда пользователь затем размещает ордер, тогда яхочу связать этот ордер как с пользователем, так и с парой адресов.

Таким образом, мои таблицы выглядят так:

Пользователь

  • Id
  • Имя
  • и т. Д.

Адрес

  • Идентификатор
  • Идентификатор пользователя (внешний ключ)
  • Улица
  • и т. Д.

Заказ

  • Id
  • UserId (внешний ключ)
  • DeliveryAddressId (внешний ключ)
  • BillingAddressId (внешний ключ)
  • и т. Д.

Можно ли настроить проверку в SQL Server таким образом, чтобы пользователь не могпри любых обстоятельствах (например, путем взлома HTML POST) отправьте Заказ с AddressId, который не связан с тем же, что и отправленный UserId.Я смотрел на «ограничения внешнего ключа» в документах, но это, кажется, не совсем то, что я ищу.

Любые предложения о том, что попробовать - или какие учебники прочитать - будутбыть наиболее ценным.

Ответы [ 3 ]

3 голосов
/ 27 мая 2011

В дополнение к первичному ключу в таблице адресов (для Id) вы должны также объявить еще одно ключевое ограничение, ограничение UNIQUE, для (Id, UserId).

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

Затем вы можете либозамените ваши существующие FK с Order на address или добавьте дополнительные, которые проверяют оба столбца

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

Как я уже сказал, вы можете добавить все это как дополнительные ограничения, если хотите.


Итак, с некоторыми незначительными изменениями имен, ваши таблицы отображаются так:

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

И пробуем это с некоторыми вставками, которые должны работать, кроме последней (где есть пользователь / адрес)несоответствие), работает:

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

Результаты:

(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.
1 голос
/ 27 мая 2011

Будет ли это иметь составной внешний ключ из Order (UserID, DeliveryAddressID) в Address (UserID, ID)? (Аналогично для BillingAddressID)

1 голос
/ 26 мая 2011

создайте триггер ON INSERT для выполнения любой дополнительной логики, которую вы хотите применить.

Недостатком будет то, что пользователь получит сообщение об ошибке, когда попытается использовать неправильный адрес ... для проактивности,Вы должны сделать эту проверку также в графическом интерфейсе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...