【问题标题】:Calculating from self-join从自联接计算
【发布时间】:2010-12-19 07:38:25
【问题描述】:

我有一个股票代码符号的值和日期列表,并想在 SQL 中计算季度回报。

CREATE TABLE `symbol_details` (
  `symbol_header_id` INT(11) DEFAULT NULL,
  `DATE` DATETIME DEFAULT NULL,
  `NAV` DOUBLE DEFAULT NULL,
  `ADJ_NAV` DOUBLE DEFAULT NULL)

对于可以正常工作的固定季度开始和结束日期:

set @quarterstart='2008-12-31';
set @quarterend='2009-3-31';

select sha, (100*(aend-abegin)/abegin) as q1_returns from
(select symbol_header_id as sha, ADJ_NAV as abegin from symbol_details
  where date=@quarterstart) as a,
(select  symbol_header_id as she, ADJ_NAV as aend from symbol_details
  where date=@quarterend) as b
where sha=she;

这会计算所有品种的所有季度回报。有时季度末是非交易日,或者股票停止营业,所以我想获得最接近季度开始和结束日期的开始和结束日期。

解决方案是通过某种 GROUP BY 语句以某种方式仅获取每个 symbol_header_id 的一个开始和一个结束季度值,例如 (1)

SET @quarterstart = '2009-03-01';
SET @quarterend = '2009-4-31';
SELECT  symbol_header_id, DATE, ADJ_NAV AS aend FROM symbol_details
WHERE
 DATE BETWEEN @quarterstart AND @quarterend
 AND symbol_header_id BETWEEN 18540 AND 18550
GROUP BY symbol_header_id asc;

这为每个 symbol_header_id 值提供了最接近季度开始日期的 ADJ_NAV 值。

最后,(2)

SET @quarterstart = '2008-12-31';
SET @quarterend = '2009-3-31';

SELECT sh1, a.date, b.date, aend, abegin, 
       (100*(aend-abegin)/abegin) AS quarter_returns FROM
(SELECT  symbol_header_id sh1, DATE, ADJ_NAV AS abegin FROM symbol_details
WHERE
 DATE BETWEEN @quarterstart AND @quarterend
 GROUP BY symbol_header_id DESC) a,

(SELECT  symbol_header_id sh2, DATE, ADJ_NAV AS aend FROM symbol_details
WHERE
 DATE BETWEEN @quarterstart AND @quarterend
 GROUP BY symbol_header_id ASC) b
WHERE sh1 = sh2;

应该计算每个品种的季度收益。

不幸的是,这不起作用。出于某种原因,当我像 (1) 那样限制 ID 时,使用了正确的开始日期和结束日期,但是当删除“AND symbol_header_id BETWEEN 18540 AND 18550”语句时,会出现相同的开始日期和结束日期。为什么????

展开 JOIN 的答案是:

SET @quarterstart = '2008-12-31';
SET @quarterend = '2009-3-31';


SELECT tq.sym                                AS sym,
       (100*(alast.adj_nav - afirst.adj_nav)/afirst.adj_nav) AS quarterly_returns
FROM

  -- First, determine first traded days ("ftd") and last traded days
  -- ("ltd") in this quarter per symbol
  (SELECT symbol_header_id AS sym,
          MIN(DATE)      AS ftd,
          MAX(DATE)      AS ltd
   FROM symbol_details
   WHERE DATE BETWEEN @quarterstart AND @quarterend
   GROUP BY 1) tq

  JOIN symbol_details afirst
  -- Second, determine adjusted NAV for "ftd" per symbol (see WHERE)
   ON afirst.DATE BETWEEN @quarterstart AND @quarterend
   AND afirst.symbol_header_id = tq.sym

  JOIN
  -- Finally, determine adjusted NAV for "ltd" per symbol (see WHERE)
    symbol_details alast
    ON alast.DATE BETWEEN @quarterstart AND @quarterend
    AND alast.symbol_header_id = tq.sym

WHERE
  afirst.date = tq.ftd
  AND
  alast.date  = tq.ltd;

【问题讨论】:

    标签: sql mysql date join


    【解决方案1】:

    更新:

    完全结合@Hogan 的建议......并对其进行测试。 :) 这EXPLAINs 更简单,应该是更好的表现。

    再次假设 ANSI_QUOTES 行为:

    SELECT tq.sym AS sym,
           (100*(alast.adj_nav - afirst.adj_nav)/afirst.adj_nav) AS quarterly_returns
    FROM
      (SELECT symbol_header_id AS sym, -- find first/last traded day ("ftd", "ltd")
              MIN("date") AS ftd,
              MAX("date") AS ltd
       FROM symbol_details
       WHERE "date" BETWEEN @quarterstart AND @quarterend
       GROUP BY 1) tq
    JOIN symbol_details afirst         -- JOIN for ADJ_NAV on first traded day
      ON tq.sym = afirst.symbol_header_id
         AND
         tq.ftd = afirst."date"
    JOIN symbol_details alast          -- JOIN for ADJ_NAV on last traded day
      ON tq.sym = alast.symbol_header_id
         AND
         tq.ltd = alast."date"
    

    原文:

    假设SET SESSION sql_mode = 'ANSI_QUOTES',试试这个:

    SELECT tq.sym                                AS sym,
           (100*(adj_end - adj_begin)/adj_begin) AS quarterly_returns
    FROM
      -- First, determine first traded days ("ftd") and last traded days
      -- ("ltd") in this quarter per symbol
      (SELECT symbol_header_id AS sym,
              MIN("date")      AS ftd,
              MAX("date")      AS ltd
       FROM symbol_details
       WHERE "date" BETWEEN @quarterstart AND @quarterend
       GROUP BY 1) tq
      JOIN
      -- Second, determine adjusted NAV for "ftd" per symbol (see WHERE)
      (SELECT symbol_header_id AS sym,
              "date"           AS adate,
              adj_nav          AS adj_begin
       FROM symbol_details) afirst
      ON afirst.sym = tq.sym
      JOIN
      -- Finally, determine adjusted NAV for "ltd" per symbol (see WHERE)
      (SELECT symbol_header_id AS sym,
              "date"           AS adate,
              adj_nav          AS adj_end
       FROM symbol_details) alast
      ON alast.sym = tq.sym
    WHERE
      afirst.adate = tq.ftd
      AND
      alast.adate  = tq.ltd;
    

    【讨论】:

    • 这看起来真的很棒,但是当我在有 200 万条记录的 MySQL 数据库上尝试它时,它永远不会完成。是否有性能优化?
    • 展开子查询到真正的连接。
    【解决方案2】:

    从以下查询展开最后一个子查询的示例:

    (注意——我没有测试)

    SELECT tq.sym                                AS sym,
           (100*(alast.adj_nav - adj_begin)/adj_begin) AS quarterly_returns
    FROM
      -- First, determine first traded days ("ftd") and last traded days
      -- ("ltd") in this quarter per symbol
      (SELECT symbol_header_id AS sym,
              MIN("date")      AS ftd,
              MAX("date")      AS ltd
       FROM symbol_details
       WHERE "date" BETWEEN @quarterstart AND @quarterend
       GROUP BY 1) tq
      JOIN
      -- Second, determine adjusted NAV for "ftd" per symbol (see WHERE)
      (SELECT symbol_header_id AS sym,
              "date"           AS adate,
              adj_nav          AS adj_begin
       FROM symbol_details) afirst
      ON afirst.sym = tq.sym
      -- Finally, determine adjusted NAV for "ltd" per symbol (see WHERE)
      LEFT JOIN symbol_details alast ON tq.sym = alast.symbol_header_id AND tg.ltd = alast."date"
    
    WHERE
      afirst.adate = tq.ftd
    

    【讨论】:

    • +1。 @Hogan,与 s/tg/tq/ 一起工作,我已经更新、进一步展开和测试。
    【解决方案3】:

    您确定在查询中使用的是 GROUP BY ... ASC 吗? GROUP BY 对满足某些条件的行进行分组,使用 ASC/DESC 没有意义。顺便说一句,在您的情况下,您的标准是 symbol_header_id 列的相等性,假设“id”表示它是一个键,有效地使组由单行组成。

    无论如何,如果您的组是真正的组,您可能会遇到问题,因为您不仅要选择分组属性和聚合函数,还要选择其他一些属性。在这种情况下,它们的价值是不可预测的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多