Можно ли заменить подзапросы, используемые в качестве скалярного значения, на CTE? - PullRequest
0 голосов
/ 22 мая 2019

Я хочу заменить длинный подзапрос, который возвращает скалярное значение или не существует, просто коротким псевдонимом, потому что я помещаю его 3 раза в инструкцию UPDATE. Подзапрос принимает последнее значение в 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;

Ошибка: рядом с «ОБНОВЛЕНИЕ»: синтаксическая ошибка

Это работает хорошо, когда я не заменяю подзапрос, но когда я пытаюсь использовать CTE, это терпит неудачу. Я работаю в режиме sql.el в Emacs.

1 Ответ

1 голос
/ 23 мая 2019

Да, это можно много почистить. Рассмотрим что-то вроде:

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;

, который, я уверен, рассчитывает те же результаты, что и ваш. (Фактическое определение таблицы и данных для работы было бы неплохо)

Также обратите внимание, что в последних выпусках Sqlite3 (3.25 и более поздних) ваш столбец SumProfit может быть легко вычислен по требованию вместо того, чтобы занимать место в таблице:

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