我有一个股票代码符号的值和日期列表,并希望计算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;
答案 0 :(得分:1)
<强>更新强>
完全纳入@ Hogan的建议......并对其进行测试。 :)这个EXPLAIN
更简单,应该是更好的表演者。
再次,假设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"
<强> ORIGINAL:强>
假设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;
答案 1 :(得分:1)
从以下查询中展开最后一个子查询的示例:
(注意 - 我没有测试)
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
答案 2 :(得分:0)
您确定在查询中使用GROUP BY ... ASC吗? GROUP BY对满足某些条件并使用ASC / DESC的行进行分组是没有意义的。顺便说一句,在你的情况下,你的标准是symbol_header_id列的相等,假设“id”表示它是一个键,有效地使组成一行。
无论如何,如果您的群组确实是群组,那么您可能会遇到问题,因为您不仅要选择分组属性和聚合函数,还要选择其他一些属性。在这种情况下,它们的价值是不可预测的。