【发布时间】:2020-11-06 11:15:35
【问题描述】:
我已经好几天没能想出一个可行的解决方案了。
我正在开发一个系统来维护和借出这些物品。
Loan 包含IEnumerable<LoanLine>,每个都指向一个Item:
到目前为止一切顺利。
当每件物品不能在同一时期借出时,棘手的部分就暴露出来了。该时期由LoanLine.PickedUp ?? Loan.DateFrom > LoanLine.Returned ?? Loan.DateTo 定义。这意味着如果LoanLine.PickedUp 为空,则应使用Loan.DateFrom 进行比较,如果LoanLine.Returned 为空,则应使用Loan.DateTo。
物品可以在外借范围之外被拾取和归还。所以可能会出现这些情况:
也应该可以“返回”,即。将LoanLine.Returned 设置为null,在这种情况下Loan.DateTo 用于再次比较。 LoanLine.PickedUp 也是如此。
还应该可以更新Loan.DateFrom 和Loan.DateTo,而前面提到的约束仍然有效。这意味着,如果对 Loan 的更新导致其中任一行重叠,而 DateTime 设置为 null,则约束将引发错误。
这是创建脚本:
create table loan
(
id int primary key identity(1, 1),
datefrom date not null,
dateto date not null,
employee_id int references employee(id) not null,
recipient_id int references employee(id) null,
note nvarchar(max) not null,
constraint c_loan_chkdates check (datefrom <= dateto)
);
create table loanlineitem
(
id int primary key identity(1, 1),
loan_id int references loan(id) on delete cascade not null,
item_id int references item(id) not null,
pickedup datetime null,
returned datetime null,
constraint uq_loanlineitem unique (loan_id, item_id),
constraint c_loanlineitem_chkdates check (returned is null or pickedup <= returned)
);
这是约束:
create function checkLoanLineItem(@itemId int, @loanId int, @pickedup datetime, @returned datetime)
returns bit
as
begin
declare @result bit = 0;
declare @from date = @pickedup;
declare @to date = @returned;
--If either @from or @to is null, fill the ones with null from loan-table
if (isnull(@from, @to) is null)
begin
select @from = isnull(@from, datefrom),
@to = isnull(@to, dateadd(d, 1, dateto))
from loan
where id = @loanId;
end
if not exists (select top 1 lli.id from loanlineitem lli
inner join loan l on lli.loan_id = l.id
where l.id <> @loanId
and lli.item_id = @itemId
and ((isnull(lli.pickedup, l.datefrom) >= @from and isnull(lli.pickedup, l.datefrom) < @to)
--When comparing datetime with date, the date's time is 00:00:00
--so one day is added to account for this
or (isnull(lli.returned, dateadd(d, 1, l.dateto)) >= @from and isnull(lli.returned, dateadd(d, 1, l.dateto)) < @to))
)
begin
set @result = 1;
end
return @result;
end;
go;
alter table loanlineitem
add constraint c_loanlineitem_checkoverlap check (dbo.checkLoanLineItem(item_id, loan_id, pickedup, returned) = 1)
go;
我可以对Loan-table 进行类似的限制,但我会在两个地方有类似的代码,如果可能的话,我希望避免。
所以我要问的是;我应该重新考虑我的架构来实现这一点,还是有一些我不熟悉的约束?
【问题讨论】:
标签: sql sql-server database-design check-constraints