SQLite UPDATE语句中的相关子查询(或等效)?

时间:2010-11-27 09:04:28

标签: sql triggers sqlite

我有一个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 barUPDATE INNER JOIN ...之类的东西,显然你是如何在其他数据库上执行的),或者,如我给出的示例,它是有效的,但具有错误的含义。 (在这种情况下,“如果存在任何带有read_free的链接类型,则在此故事中设置read_free,无论该故事是否具有该类型的链接)

如果除了简单地解决问题之外还存在一个更简洁,更简洁的UPDATE短语,我也很高兴知道这一点。即使这确实有效,但与其他触发器中最糟糕的触发器相比,这是一个非常难看的解决方案。

2 个答案:

答案 0 :(得分:1)

而不是UPDATE,您可以使用INSERT OR REPLACE吗?与UPDATE不同,INSERT OR REPLACE会接受嵌入式SELECT,因此您可以执行UPDATE foo AS barUPDATE 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颁发奖金有两个原因:首先,因为如果没有他让我从那个车辙中走出来,我就永远不会想出这个答案。其次,因为如果我的要求与我原先相信的那样,那么他的回答将是最好的。