Reputation: 113
I would like to create table with CHECK constraint, where CHECK calls an user defined scalar function. I have read on multiple sites that it is possible, also that it has bad performance. Even though I would like to do it.
I have this table
CREATE TABLE [book_history] (
id int NOT NULL IDENTITY PRIMARY KEY,
user_id int NOT NULL,
library_id int NOT NULL,
book_id int NOT NULL,
borrow_time datetime DEFAULT GETDATE(),
return_policy datetime DEFAULT DATEADD(DAY, 30, GETDATE()),
return_time datetime,
CHECK (dbo.fn_check_duplicate(user_id, library_id, book_id) = 0)
);
and function
DROP FUNCTION IF EXISTS fn_check_duplicate
GO
CREATE FUNCTION fn_check_duplicate (@user_id int, @library_id int, @book_id int)
RETURNS int
BEGIN
RETURN (SELECT COUNT(*) FROM [book_history] WHERE user_id = @user_id AND library_id = @library_id AND book_id = @book_id AND return_time IS NULL)
END
GO
When I try to insert new row into this book_history
table (which is empty), I get an error saying The INSERT statement conflicted with the CHECK constraint "CK__book_history__267ABA7A". The conflict occurred in database "library", table "dbo.book_history".
COUNT is supposed to return int data type based on MSDN documentation.
I am owner of both, the table and the function.
Can anyone tell me what am I doing wrong?
Upvotes: 1
Views: 135
Reputation: 38043
Change it to check (dbo.fn_check_duplicate(user_id, library_id, book_id) = 1)
The check
is going to look at the state of the table after the insert, so you want the count to be 1.
Test it on rextester: http://rextester.com/AWDNP40594 by uncommenting the second insert.
You can also replace this slow check constraint with a filtered unique index like so:
create unique nonclustered index uix_book_history_user_library_book
on dbo.book_history (user_id, library_id, book_id)
where return_time is null
This might be more of what you are trying to do, if each book_id is an individual book:
create unique nonclustered index uix_book_history_library_book
on dbo.book_history (library_id, book_id)
where return_time is null
Because this would allow a book to only be checked out by one user at a time.
Upvotes: 4