是否可以用 CTE 替换用作标量值的子查询?

Is it possible to replace sub-queries, used as a scalar value, with CTE?

我想用一个短别名替换一个 return 标量值或不存在的长子查询,因为我将它放在 UPDATE 语句中 3 次。子查询取 UncoveredLoss 中的最后一个值,如果有的话,根据最后一个 UncoveredLoss 值在更新的行中计算新的 UncoveredLoss 值。

是非关联查询,但是用在SELECT子句中,而不是在FROM子句中。也许我应该以某种方式修改触发器中的 UPDATE 语句。

工作代码:

CREATE TRIGGER Result

UPDATE OF Win ON Log

BEGIN
    UPDATE Log
    SET Profit = CASE 
            WHEN NEW.Win = 0
                THEN - Stake
            WHEN NEW.Win = 1
                THEN Rate * Stake / 100
            WHEN NEW.Win = 2
                THEN 0
            END
    WHERE ID = OLD.ID;

    UPDATE Log
    SET SumProfit = (
            SELECT Sum(Profit)
            FROM (
                SELECT StrategyAccountID
                    ,Profit
                FROM Log
                WHERE DATE <= NEW.DATE
                )
            GROUP BY StrategyAccountID
            HAVING StrategyAccountID = NEW.StrategyAccountID
            )
    WHERE ID = NEW.ID;

    UPDATE Log
    SET UncoveredLoss = CASE 
            WHEN EXISTS (
                    SELECT UncoveredLoss
                    FROM Log
                    WHERE DATE < NEW.DATE
                        AND StrategyAccountID = NEW.StrategyAccountID
                    ORDER BY DATE DESC LIMIT 1
                    )
                AND (
                    SELECT UncoveredLoss
                    FROM Log
                    WHERE DATE < NEW.DATE
                        AND StrategyAccountID = NEW.StrategyAccountID
                    ORDER BY DATE DESC LIMIT 1
                    ) + NEW.Profit < 0
                THEN (
                        SELECT UncoveredLoss
                        FROM Log
                        WHERE DATE < NEW.DATE
                            AND StrategyAccountID = NEW.StrategyAccountID
                        ORDER BY DATE DESC LIMIT 1
                        ) + NEW.Profit
            WHEN NOT EXISTS (
                    SELECT UncoveredLoss
                    FROM Log
                    WHERE DATE < NEW.DATE
                        AND StrategyAccountID = NEW.StrategyAccountID
                    ORDER BY DATE DESC LIMIT 1
                    )
                AND NEW.Profit < 0
                THEN NEW.Profit
            ELSE 0
            END
    WHERE ID = NEW.ID;
END;

使用 CTE 简单替换子查询不起作用:

CREATE TRIGGER Result

UPDATE OF Win ON Log

BEGIN
    UPDATE Log
    SET Profit = CASE 
            WHEN NEW.Win = 0
                THEN - Stake
            WHEN NEW.Win = 1
                THEN Rate * Stake / 100
            WHEN NEW.Win = 2
                THEN 0
            END
    WHERE ID = OLD.ID;

    UPDATE Log
    SET SumProfit = (
            SELECT Sum(Profit)
            FROM (
                SELECT StrategyAccountID
                    ,Profit
                FROM Log
                WHERE DATE <= NEW.DATE
                )
            GROUP BY StrategyAccountID
            HAVING StrategyAccountID = NEW.StrategyAccountID
            )
    WHERE ID = NEW.ID;

    WITH Loss
    AS (
        SELECT UncoveredLoss
        FROM Log
        WHERE DATE < NEW.DATE
            AND StrategyAccountID = NEW.StrategyAccountID
        ORDER BY DATE DESC LIMIT 1
        )
    UPDATE Log
    SET UncoveredLoss = CASE 
            WHEN EXISTS (Loss)
                AND (Loss) + NEW.Profit < 0
                THEN (Loss) + NEW.Profit
            WHEN NOT EXISTS (Loss)
                AND NEW.Profit < 0
                THEN NEW.Profit
            ELSE 0
            END
    WHERE ID = NEW.ID;
END;

Error: near "UPDATE": syntax error

当我不替换子查询时它工作得很好,但是当我尝试使用 CTE 时它失败了。我在 Emacs 中以 sql.el 模式工作。

是的,可以清理很多。考虑这样的事情:

CREATE TRIGGER Result UPDATE OF Win ON Log BEGIN
  UPDATE Log
  SET SumProfit = (SELECT sum(Profit)
                   FROM Log
                   WHERE Date <= NEW.Date AND StrategyAccountID = NEW.StrategyAccountID)
    , UncoveredLoss = ifnull((SELECT min(UncoveredLoss + NEW.Profit, 0)
                              FROM Log
                              WHERE Date < NEW.Date AND StrategyAccountID = NEW.StrategyAccountID
                              ORDER BY Date DESC
                              LIMIT 1), 0)
  WHERE ID = NEW.ID;
END;

我很确定计算的结果与您的相同。 (实际样本 table 定义和数据会很好)

另请注意,在最近的 Sqlite3 版本(3.25 及更高版本)中,您的 SumProfit 列可以很容易地按需计算,而不是占用 table 中的 space:

SELECT *
     , sum(profit) OVER (PARTITION BY StrategyAccountID ORDER BY Date) AS SumProfit
FROM Log;