是否可以在 SQLite3 中声明一个触发器,强制用户在 UPDATE 语句中明确提供一个值?
假设我们有一个 Article
table :
CREATE TABLE Article (
Id INTEGER PRIMARY KEY,
Title TEXT NOT NULL UNIQUE,
Content TEXT,
UserInserted TEXT NOT NULL,
UserUpdated TEXT
);
我可以声明以下触发器,该触发器禁止
UserUpdated
列的空值:CREATE TRIGGER IF NOT EXISTS Trig_Article_BEFORE_UPDATE
BEFORE UPDATE OF Title, Content ON Article
BEGIN
SELECT
CASE
WHEN new.UserUpdated IS NULL THEN RAISE(ABORT, 'UserUpdated must not be NULL.')
WHEN length(new.UserUpdated) = 0 THEN RAISE(ABORT, 'UserUpdated must not be NULL.')
END;
END;
插入按预期工作:
INSERT INTO Article(Title, Content, UserInserted) VALUES('Foo', '', '<user_A>');
在不提供
UserUpdated
的情况下进行更新首先也可以:UPDATE Article SET Content = 'Bar' WHERE Id = 1;
-- Error: UserUpdated must not be NULL.
UPDATE Article SET Content = 'Bar', UserUpdated = '' WHERE Id = 1;
-- Error: UserUpdated must not be NULL.
但是一旦一个
UserUpdated
已设置它不再需要显式提供该列。UPDATE Article SET Content = 'Bar', UserUpdated = '<user_B>' WHERE Id = 1;
UPDATE Article SET Content = 'Foo Bar' WHERE Id = 1;
-- No error
有没有办法声明一个触发器,以便最后一条语句也抛出一个错误?
更新 22.11.2019
感谢 C Perkins' answer我想出了一个使用额外列的解决方案。
一个额外的列
CurrentUser
已添加到 Article
:CREATE TABLE Article (
-- ...
CurrentUser TEXT
);
一个
BEFORE UPDATE
触发器确保设置此列:CREATE TRIGGER IF NOT EXISTS Trig_Article_BEFORE_UPDATE
BEFORE UPDATE ON Article
WHEN old.Title <> new.Title OR
old.Content <> new.Content OR
old.CurrentUser <> new.CurrentUser
BEGIN
SELECT
CASE
WHEN new.CurrentUser IS NULL THEN RAISE(ABORT, 'CurrentUser must not be NULL.')
WHEN length(new.CurrentUser) = 0 THEN RAISE(ABORT, 'CurrentUser must not be NULL.')
END;
END;
一个
AFTER UPDATE
触发器(如果 CurrentUser
不为空)从 CurrentUser
复制值至UserUpdated
并清除 CurrentUser
再次。CREATE TRIGGER IF NOT EXISTS Trig_Article_AFTER_UPDATE
AFTER UPDATE ON Article
WHEN new.CurrentUser IS NOT NULL
BEGIN
UPDATE Article SET UserUpdated = new.CurrentUser, CurrentUser = NULL WHERE Id = new.Id;
END;
为了防止直接更新
UserUpdated
使用另一个触发器:CREATE TRIGGER IF NOT EXISTS Trig_Article_UserUpdated_BEFORE_UPDATE
BEFORE UPDATE ON Article
WHEN old.UserUpdated <> new.UserUpdated AND
old.CurrentUser IS NULL
BEGIN
SELECT RAISE(ABORT, 'You must not UPDATE UserUpdated.');
END;
毕竟我得到了想要的行为。每次
Content
或 Title
已更新列 CurrentUser
必须在更新声明和 UserUpdated
中明确提供反射(reflect)最后一个更新值的用户。
最佳答案
利用另一个仅更新列。以下是所涉及内容的简要概述:
UserUpdateONLY
New.UserUpdateONLY IS NOT NULL AND length(New.UpdateUpdateONLY) != 0
那New.UserUpdated == Old.UserUpdated OR (New.UserUpdated IS NULL AND Old.UserUpdated IS NULL)
避免两列与更新数据发生矛盾(如果任一条件为假,则引发错误)。 SET UserUpdateONLY = NULL, UserUpdated = NEW.UserUpdateONLY
唯一可能的问题是,如果在普通列上允许 NULL 更新,那么明显的“触发”值在这种情况下将不起作用。如果这可能是个问题,请将不太可能的值存储为 UserUpdateONLY 列的默认值,例如
'<NOT VALID>'
以便始终检测到新的有效。
关于database - 使用 SQLite 触发器强制显式设置列值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58977783/