SQLite:使用WITH的SQLite-触发器的解决方法

e5njpo68  于 2023-01-21  发布在  SQLite
关注(0)|答案(1)|浏览(132)

我正在进行一个项目,用嵌入式设备监控生产线的停机时间。我想通过用户可以配置的通用规则自动确认这些停机时间。我想使用TRIGGER,但在UPDATE附近出现语法错误,尽管文档中说使用WITH语句应该没问题。

CREATE TRIGGER autoAcknowledge
    AFTER UPDATE OF dtEnd ON ackGroups
    FOR EACH ROW
    WHEN old.dtEnd IS NULL AND new.dtEnd IS NOT NULL
    BEGIN
        WITH sub1(id, stationId, groupDur) AS (
            SELECT MIN(d.id), d.station,
                strftime('%s', ag.dtEnd) - strftime('%s', ag.dtStart) 
                FROM ackGroups AS ag
                LEFT JOIN downtimes AS d on d.acknowledge = ag.id
                WHERE ag.id = old.id
                GROUP BY ag.id ),
            sub2( originId, groupDur, reasonId, above, ruleDur) AS (
                SELECT sub1.stationId, sub1.groupDur, aar.reasonId, aar.above, aar.duration
                FROM sub1
                LEFT JOIN autoAckStations AS aas ON aas.stationId = sub1.stationId
                LEFT JOIN autoAckRules AS aar ON aas.autoAckRuleId = aar.id
                ORDER BY duration DESC )
        UPDATE ackGroups SET (reason, dtAck, origin)=(
            SELECT reasonId, datetime('now'), originId
            FROM sub2 as s
            WHERE ( s.ruleDur < s.groupDur AND above = 1 ) OR (s.ruleDur > s.groupDur AND above = 0)
            LIMIT 1
        )
        WHERE id = old.id;
    END

背景:首先我们有停工时间表。每条生产线由多个称为工位的部分组成。每个工位可以启动生产线停工时间,也可以与其他工位的停工时间重叠。

CREATE TABLE "downtimes" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
    "station" integer NOT NULL, 
    "acknowledge" integer, 
    "dtStart" datetime NOT NULL, 
    "dtEnd" datetime, 
    "dtLastModified" datetime)

重叠的停机时间被分组到确认组,在停机时间使用TRIGGER AFTER INSERT正确设置确认ID或创建新组。

CREATE TABLE "ackGroups" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
    "reason" integer, 
    "dtAck" datetime, 
    "dtStart" datetime NOT NULL, 
    "dtEnd" datetime, 
    "line" integer NOT NULL, 
    "origin" integer)

autoAckRules表表示配置。用户决定是否应将规则应用于高于或低于某个值的持续时间,以及应使用哪个rasonId进行确认。

CREATE TABLE "autoAckRules" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
    "description" text NOT NULL, 
    "reasonId" integer NOT NULL, 
    "above" bool NOT NULL, 
    "duration" real NOT NULL)

autoAckStations表用于管理M:N关系。每个规则允许多个工作站启动ackGroup。

CREATE TABLE autoAckStations (
        autoAckRuleId INTEGER NOT NULL,
        stationId INTEGER NOT NULL,
        PRIMARY KEY ( autoAckRuleId, stationId )
    )

当最后一次停机结束时,ackGroupsdtEnd被设置为datetime('now'),并且触发器被激发以检查是否存在适合的autoAckRule。
如果我用SELECT .. FROM( SELECT .. FROM(SELECT .. FROM )))级联替换子选择,是否有一种好方法可以避免两次写入和求值?
或者我错过了什么愚蠢的事情?

2vuwiymt

2vuwiymt1#

triggers内部的语句不支持公用表表达式。您需要将CTE转换为子查询,例如

CREATE TRIGGER autoAcknowledge
AFTER UPDATE OF dtEnd ON ackGroups
FOR EACH ROW
WHEN old.dtEnd IS NULL AND new.dtEnd IS NOT NULL
BEGIN
    UPDATE ackGroups 
       SET (reason, dtAck, origin)= (
    SELECT reasonId, datetime('now'), originId
      FROM (SELECT sub1.stationId AS originId,
                   sub1.groupDur  AS groupDur, 
                   aar.reasonId   AS reasonId, 
                   aar.above      AS above, 
                   aar.duration   AS ruleDur
             FROM (SELECT MIN(d.id) AS id,
                         d.station AS stationId,
                         strftime('%s', ag.dtEnd) - strftime('%s', ag.dtStart) AS groupDur 
                    FROM ackGroups AS ag
                    LEFT 
                    JOIN downtimes AS d 
                      ON d.acknowledge = ag.id
                   WHERE ag.id = old.id
                   GROUP BY ag.id ) AS sub1
            LEFT 
            JOIN autoAckStations AS aas 
              ON aas.stationId = sub1.stationId
            LEFT 
            JOIN autoAckRules AS aar 
              ON aas.autoAckRuleId = aar.id
           ORDER BY duration DESC) as s
        WHERE ( s.ruleDur < s.groupDur AND above = 1 ) OR (s.ruleDur > s.groupDur AND above = 0)
        LIMIT 1
    );
END;

相关问题