我有两个表,使用以下SQL查询创建:
CREATE TABLE user
(
user_email varchar(255) not null primary key,
--other unimportant columns
subscription_start date not null,
subscription_end date,
CONSTRAINT chk_end_start CHECK (subscription_start != subscription_end)
)
CREATE TABLE action
(
--unimportant columns
user_email varchar(255) not null,
action_date date not null,
CONSTRAINT FK_user FOREIGN KEY (user_email) REFERENCES user(user_email)
)
我想要做的是确保某种检查约束,action_date
介于subscription_start
和subscription_end
之间。
答案 0 :(得分:3)
我总是试图在可能的情况下避免触发器,所以我想在混合物中添加替代品。您可以使用索引视图在此处验证数据。首先,您需要创建一个新表,它只包含两行:
CREATE TABLE dbo.Two (Number INT NOT NULL);
INSERT dbo.Two VALUES (1), (2);
现在您可以创建索引视图,我已使用ActionID
作为Action
表的隐含主键,但您可能需要更改此内容:
CREATE VIEW dbo.ActionCheck
WITH SCHEMABINDING
AS
SELECT a.ActionID
FROM dbo.[User] AS u
INNER JOIN dbo.[Action] AS a
ON a.user_email = u.user_email
CROSS JOIN dbo.Two AS t
WHERE a.Action_date < u.subscription_start
OR a.Action_date > u.subscription_end
OR t.Number = 1;
GO;
CREATE UNIQUE CLUSTERED INDEX UQ_ActionCheck_ActionID ON dbo.ActionCheck (ActionID);
因此,您的视图将始终为每个操作返回一行(t.Number = 1
子句),但是,如果操作日期超出订阅日期,则将返回dbo.Two
中number = 2的行,这将导致ActionID
的重复,这将违反索引的唯一约束,因此将停止插入。 e.g:
INSERT [user] (user_email, subscription_start, subscription_end)
VALUES ('test@test.com', '20140101', '20150101');
INSERT [Action] (user_email, action_date) VALUES ('test@test.com', '20140102');
-- WORKS FINE UP TO THIS POINT
-- THIS NEXT INSERT THROWS AN ERROR
INSERT [Action] (user_email, action_date) VALUES ('test@test.com', '20120102');
Msg 2601,Level 14,State 1,Line 1
无法在对象&#39; dbo.ActionCheck&#39;中插入重复的键行。具有唯一索引&#39; UQ_ActionCheck_ActionID&#39;。重复键值为(6)。
声明已经终止。
答案 1 :(得分:2)
使用检查约束不可能这样做,因为检查约束只能引用同一个表中的列。此外,外键约束仅支持等连接。
如果必须在数据库级别而不是应用程序级别执行此检查,则可以在action-table上使用INSERT / UPDATE上的触发器执行此操作。每次插入或更新记录时,都要检查action_date是否位于user-table上相应的subscription_start / end日期内。如果不是这种情况,则使用RAISERROR函数标记无法插入/更新行。
CREATE TRIGGER ActionDateTrigger ON tblaction
AFTER INSERT, UPDATE
AS
IF NOT EXISTS (
SELECT * FROM tbluser u JOIN inserted i ON i.user_email = u.user_email
AND i.action_date BETWEEN u.subscription_start AND u.subscription_end
)
BEGIN
RAISERROR ('Action_date outside valid range', 16, 1);
ROLLBACK TRANSACTION;
END
答案 2 :(得分:0)
解决方案是这样的:
- 在表操作中创建一个检查约束,该约束调用如下函数:check(dbo.FcToCheckValidity(user_email,action_date)= 1)
- dbo.FcToCheckValidity函数类似于:
- 返回BIT的FcToCheckValidity(user_email,action_date)
- 查询用户表和:如果在subscription_start和subscription_end之间为正确的用户输入action_date,则返回1,否则为0。
答案 3 :(得分:0)
实际上,当表中的数据发生变化时,索引视图需要“重新索引”。如果它是一个相当复杂的视图或有很多行,性能将受到影响。我也相信在这种情况下触发器会表现得更好......