You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL DISTINCT按permno去重失效及GROUP BY计算总收益报错解决

CRSP股票收益查询问题解决方案

问题1:DISTINCT无法按permno完成去重

根本原因

  • DISTINCT的去重逻辑是对SELECT语句中所有返回字段的组合做整体去重,而非仅针对permno字段去重。
  • 你的关联条件仅匹配了permno,未关联crsp.dsenames表的名称生效时间范围。该表本身记录了单个permno全生命周期内不同时段的ticker、comnam变更记录,直接关联会让同一permno的日度收益和所有历史名称记录交叉匹配,产生多条同permno、同ret但不同ticker/comnam的行。由于这些行的字段组合存在差异,DISTINCT无法将其识别为重复行。

解决方法

  • 关联crsp.dsenames时补充时间匹配规则,让交易日期落在名称的有效起止区间内(CRSP表自带namedt(名称生效日)、namendt(名称失效日)字段),从根源上过滤不相关的历史名称记录。
  • 若匹配时间后仍存在同permno的多条名称记录,通过窗口函数为每个permno下的名称记录按生效时间打序号,仅取最新生效的唯一一行,替代无差别的DISTINCT。

MySQL兼容的基础去重查询示例

SELECT permno, date, ret, ticker, comnam
FROM (
  SELECT 
    a.permno, 
    a.date, 
    a.ret, 
    b.ticker, 
    b.comnam,
    ROW_NUMBER() OVER (PARTITION BY a.permno, a.date ORDER BY b.namedt DESC) AS rn
  FROM crsp.dsf a 
  INNER JOIN crsp.dsenames b 
    ON a.permno = b.permno
    -- 匹配名称有效时间范围
    AND a.date BETWEEN b.namedt AND b.namendt
  WHERE a.date BETWEEN '2020-11-09' AND '2020-11-10'
) t
WHERE rn = 1

问题2:GROUP BY计算总收益时报错

根本原因

  • 原查询的SELECT子句中,comnam字段和聚合计算字段exp(sum(log(1+ret)))之间缺失逗号分隔,会直接触发语法解析错误。
  • MySQL开启only_full_group_by严格模式时,SELECT返回的字段要么是GROUP BY中包含的分组字段,要么是被聚合函数包裹的计算值。你在语句中同时返回了未做聚合的date、ret、ticker、comnam字段,且仅按permno分组,会直接触发语法报错。
  • 查询逻辑存在偏差:你需要计算的是区间总收益、并按总收益排序筛选最差的20只股票,但原语句直接使用单日ret排序,选出的结果并非区间维度的最差表现股票。

解决方法

  • 拆分查询逻辑:先按permno分组计算区间复利总收益,按总收益升序排序(收益越低表现越差)取前20只标的,再关联名称表匹配对应的股票代码、公司名。
  • 总收益计算的exp(sum(log(1+ret)))为CRSP官方推荐的复利计算写法,注意需要减1得到净收益率,否则计算结果为本利和而非实际收益。

MySQL兼容的完整查询示例

WITH stock_lowest_return AS (
  -- 第一步:计算每个permno在目标区间的总收益,筛选收益最低的20只
  SELECT
    permno,
    EXP(SUM(LN(1 + ret))) - 1 AS total_return
  FROM crsp.dsf
  WHERE date BETWEEN '2020-11-09' AND '2020-11-10'
  GROUP BY permno
  -- 按总收益从低到高排序,获取表现最差的标的
  ORDER BY total_return ASC
  LIMIT 20
), name_mapping AS (
  -- 预先生成每个permno对应的最新有效名称
  SELECT permno, ticker, comnam
  FROM (
    SELECT
      permno,
      ticker,
      comnam,
      ROW_NUMBER() OVER (PARTITION BY permno ORDER BY namedt DESC) AS rn
    FROM crsp.dsenames
  ) t
  WHERE rn = 1
)
-- 关联返回最终结果
SELECT
  s.permno,
  s.total_return,
  nm.ticker,
  nm.comnam
FROM stock_lowest_return s
INNER JOIN name_mapping nm
  ON s.permno = nm.permno

内容的提问来源于stack exchange,提问作者Michale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 19:06:02