【问题标题】:How to add calculated column with LAG in SQL?如何在 SQL 中添加带有 LAG 的计算列?
【发布时间】:2021-07-26 21:43:46
【问题描述】:

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

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;

我将来自theseanswers 的代码放在一起,但我仍然收到“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;

【问题讨论】:

  • 钱通常是十进制
  • 也就是说,这对我来说看起来很优雅
  • 我认为您应该为此使用VIEW,而不是计算列。我不相信可以在计算列中使用窗口函数,但我可能错了。
  • 请编辑您的问题以指定您使用的 MySQL 版本。
  • DOUBLE 不能准确地表示所有十进制值,您应该在使用它之前阅读浮点舍入错误。或者,更合适的是,使用 DECIMAL(9,2) 之类的东西,它可以在 4 个字节中保存高达 9,999,999.99 的值。

标签: mysql pandas quantitative-finance


【解决方案1】:

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

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

  • 生成的列的值是根据列定义中包含的表达式计算得出的。
  • 生成的列表达式必须遵守以下规则
    • [...]
    • 不允许使用子查询。

相反,您可以使用VIEW 来执行此操作。然后,您的应用程序代码或报告将查询视图 (prices_with_delta),而不是基表 (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

【讨论】:

  • 答案看起来不错,除了重复的“如前”声明。
  • @cona 感谢您发现这一点
  • 有没有办法避免创建我认为违反常态的prior 列?我不确定常态规则是否适用于 VIEW。
  • @cona“违反常态”并不是真正的事情(我知道您的意思是“常态”,但“违反”它不是错误或任何非法或导致数据库崩溃)?无论如何,Normal Form 只适用于存储,不适用于视图。
猜你喜欢
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多