我有一个SQLite3数据库,为了优化性能,使用由触发器保持最新的计算列。
我现在正在尝试添加一个类似于此(未经测试但可能有效)的SQLAlchemy ORM代码的触发器
story.read_free = any(link.link_type.read_free for link in story.links)
...但我无法弄清楚如何将其表达为UPDATE
条款。这是我到目前为止所得到的:
CREATE TRIGGER IF NOT EXISTS update_link_type AFTER UPDATE ON link_types
FOR EACH ROW WHEN old.read_free <> new.read_free BEGIN
UPDATE stories SET
read_free = CASE WHEN (
SELECT 1 FROM links as l, link_types as lt WHERE lt.id = new.id AND l.link_type_id = lt.id AND l.story_id = stories.id
) THEN 1 ELSE 0 END
WHERE id = (SELECT story_id from links as l, link_types as lt WHERE l.link_type_id = lt.id AND lt.id = new.id)
;
END;
我的具体问题是我无法弄清楚如何确保CASE
中的子查询是相关的。
SQLite拒绝语法(UPDATE foo AS bar
和UPDATE INNER JOIN ...
之类的东西,显然你是如何在其他数据库上执行的),或者,如我给出的示例,它是有效的,但具有错误的含义。 (在这种情况下,“如果存在任何带有read_free的链接类型,则在此故事中设置read_free,无论该故事是否具有该类型的链接)
如果除了简单地解决问题之外还存在一个更简洁,更简洁的UPDATE
短语,我也很高兴知道这一点。即使这确实有效,但与其他触发器中最糟糕的触发器相比,这是一个非常难看的解决方案。
答案 0 :(得分:1)
而不是UPDATE
,您可以使用INSERT OR REPLACE
吗?与UPDATE
不同,INSERT OR REPLACE
会接受嵌入式SELECT
,因此您可以执行UPDATE foo AS bar
或UPDATE INNER JOIN
样式的操作。您的SELECT
恰好会在stories
中生成重复的行,而只需要更改您需要更改的列。
答案 1 :(得分:0)
在撰写INSERT OR REPLACE
Robie建议的时候(使用REPLACE
别名来简化任何可能的未来MySQL端口),我意识到我的思想陷入了困境,做出了错误的假设,并且过度复杂化问题。 (可能在睡眠不足的情况下开始研究它,然后从未质疑我的初步结论)
然后,我能够重新构建我的UPDATE
,只需要一个JOIN
(SQLite也是not supported),然后将其重写为WHERE
子查询。
这是最终的触发因素:
CREATE TRIGGER IF NOT EXISTS update_link_type AFTER UPDATE ON link_types
FOR EACH ROW WHEN old.read_free <> new.read_free BEGIN
UPDATE stories SET read_free = new.read_free
WHERE id IN (SELECT story_id FROM links WHERE link_type_id = new.id)
;
END;
更清洁,更易于维护。
我正在向Robie颁发奖金有两个原因:首先,因为如果没有他让我从那个车辙中走出来,我就永远不会想出这个答案。其次,因为如果我的要求与我原先相信的那样,那么他的回答