如何在 SQL 中添加具有 LAG 的计算列?

How to add calculated column with LAG in SQL?

我有一个 MySQL table(版本 8.0.26)和股票价格,想计算对数价格变化以供将来分析。这是我的 table 和数据。

CREATE TABLE `prices` (
  `ticker` varchar(7) NOT NULL,
  `date` datetime NOT NULL,
  `price` double DEFAULT NULL,
  PRIMARY KEY (`ticker`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci


INSERT INTO `sandbox`.`prices` (`ticker`, `date`, `price`) VALUES ('A', '2021-01-01', '10');
INSERT INTO `sandbox`.`prices` (`ticker`, `date`, `price`) VALUES ('A', '2021-01-02', '10.1');
INSERT INTO `sandbox`.`prices` (`ticker`, `date`, `price`) VALUES ('A', '2021-01-03', '11');
INSERT INTO `sandbox`.`prices` (`ticker`, `date`, `price`) VALUES ('B', '2021-01-01', '50');
INSERT INTO `sandbox`.`prices` (`ticker`, `date`, `price`) VALUES ('B', '2021-01-02', '51.5');
INSERT INTO `sandbox`.`prices` (`ticker`, `date`, `price`) VALUES ('B', '2021-01-03', '49');

我可以编写此查询,但该列未保存。

SELECT *, LN(price / lag(price, 1) OVER (PARTITION BY ticker)) AS ln_open_return FROM sandbox.prices;

我将 these 中的这段代码放在一起,但我仍然收到“1064 语法错误:'WITH' 在此位置无效。需要一个表达式。”

ALTER TABLE sandbox.prices
ADD COLUMN ln_change DOUBLE AS (
WITH temp AS (
 SELECT
  *,
  LAG(price, 1) OVER(PARTITION BY ticker ORDER BY date) AS prior
  FROM sandbox.prices
)
SELECT
    *,
   COALESCE(LN(price / prior)) AS ln_change
FROM temp) PERSISTED;

TABLE(如 CREATE TABLEALTER TABLE)中的计算列(又名计算列,又名生成列)不能包含查询,它们只能是从其他派生的表达式列在同一行。

https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html

  • Values of a generated column are computed from an expression included in the column definition.
  • Generated column expressions must adhere to the following rules
    • [...]
    • Subqueries are not permitted.

相反,您可以使用 VIEW 执行此操作。然后您的应用程序代码或报告将查询视图 (prices_with_delta),而不是基础 table (prices):

CREATE VIEW sandbox.prices_with_delta AS

SELECT
    p2.*,
    COALESCE( LN( p2.price / p2.prior ) ) AS ln_change
FROM
    (
        SELECT
            p.*,
            LAG( p.price, 1 ) OVER( PARTITION BY p.ticker ORDER BY p.date ) AS prior
        FROM
            sandbox.prices AS p
    ) AS p2