检查项目是否链接不正确 - 这是“外键约束”的用途吗?

时间:2011-05-26 15:57:06

标签: sql sql-server constraints azure-sql-database

我目前正在使用SQLAzure。

我正在设置一个设计,每个用户都有一些地址

当用户然后下订单时,我想将该订单链接到用户和几个地址。

所以我的表格如下:

  

用户

     
      
  • 编号
  •   
  • 名称
  •   
  •   
     

地址

     
      
  • 编号
  •   
  • UserId(外键)
  •   
  •   
  •   
     

顺序

     
      
  • 编号
  •   
  • UserId(外键)
  •   
  • DeliveryAddressId(外键)
  •   
  • BillingAddressId(外键)
  •   
  •   

有没有办法我可以在SQL Server中设置一个检查,以便用户在任何情况下都不能(例如通过黑客攻击HTML POST)提交一个带有AddressId的订单,该订单没有链接到与提交了UserId。我看过文档中的“外键约束”,但这似乎并不是我想要的。

任何关于尝试什么的建议 - 或者要阅读的教程 - 都会受到最高的赞赏。

3 个答案:

答案 0 :(得分:3)

除了地址表中的主键(在Id上),还应该在(Id,UserId)上声明另一个键约束,UNIQUE约束。

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

然后,您可以将订单中的现有FK替换为地址,或添加其他FK,以检查两列

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 :(得分:1)

创建一个ON INSERT触发器,以执行您要强制执行的任何其他逻辑。

缺点是用户在尝试使用错误地址时会收到错误消息...要主动,您还应该在GUI中进行此检查。

答案 2 :(得分:1)

从Order(UserID,DeliveryAddressID)到Address(UserID,ID)的复合外键是否可以执行此操作? (类似于BillingAddressID)