我有一张名为prices
的表格,其中包含我每天跟踪的股票收盘价。
这是架构:
CREATE TABLE `prices` (
`id` int(21) NOT NULL auto_increment,
`ticker` varchar(21) NOT NULL,
`price` decimal(7,2) NOT NULL,
`date` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `ticker` (`ticker`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2200 ;
我正在尝试计算今天和昨天价格值大于0的任何物品的价格下降百分比。随着时间的推移,这张桌子将是巨大的,我担心性能。我认为这必须在MySQL方面而不是PHP方面完成,因为这里需要LIMIT
。
我如何处理最后2个日期并在MySQL中进行%drop计算?
非常感谢任何建议。
答案 0 :(得分:4)
我立即看到的一个问题是使用日期的时间戳数据类型,这会使您的SQL查询复杂化有两个原因 - 您必须使用范围或转换为where子句中的实际日期,但更重要的是,既然你表示你对今天的收盘价和昨天的收盘价感兴趣,你就必须跟踪市场开盘的日子 - 所以星期一的查询不同于星期二 - 星期五和市场的任何一天关闭假期也必须考虑。
我会添加一个像mktDay这样的列,并在每天市场营业时增加它。另一种方法可能是包含一个'previousClose'列,使您的计算变得微不足道。我意识到这违反了正常形式,但它在您的查询中节省了昂贵的自我加入。
如果您无法更改结构,那么您将进行自我加入以获得昨天的结束,如果您愿意,您可以通过该%变化来计算变化百分比和顺序。
下面是Eric的代码,清理了一下它在运行mysql 5.0.27的服务器上执行的程序
select
p_today.`ticker`,
p_today.`date`,
p_yest.price as `open`,
p_today.price as `close`,
((p_today.price - p_yest.price)/p_yest.price) as `change`
from
prices p_today
inner join prices p_yest on
p_today.ticker = p_yest.ticker
and date(p_today.`date`) = date(p_yest.`date`) + INTERVAL 1 DAY
and p_today.price > 0
and p_yest.price > 0
and date(p_today.`date`) = CURRENT_DATE
order by `change` desc
limit 10
请注意后面的标记,因为您的一些列名称和Eric的别名是保留字。
另请注意,对第一个表使用where子句将是一个较便宜的查询 - 首先执行get的位置,并且只需要尝试自行连接大于零且具有今天日期的行
select
p_today.`ticker`,
p_today.`date`,
p_yest.price as `open`,
p_today.price as `close`,
((p_today.price - p_yest.price)/p_yest.price) as `change`
from
prices p_today
inner join prices p_yest on
p_today.ticker = p_yest.ticker
and date(p_today.`date`) = date(p_yest.`date`) + INTERVAL 1 DAY
and p_yest.price > 0
where p_today.price > 0
and date(p_today.`date`) = CURRENT_DATE
order by `change` desc
limit 10
答案 1 :(得分:3)
CREATE TABLE `market_days` (
`market_day` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`date` DATE NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY USING BTREE (`market_day`),
UNIQUE KEY USING BTREE (`date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0
;
随着更多的市场日期过去,表格中只有INSERT
个新的date
值。 market_day
会相应增加。
在插入prices
数据时,请查看给定LAST_INSERT_ID()
的{{1}}或相应值以查找过去的值。
对于date
表本身,您可以使用有用的prices
和无SELECT
列更有效地进行存储,INSERT
和PRIMARY KEY
操作。在下面的架构中,您的AUTO_INCREMENT
包含本质上有用的信息,而不仅仅是识别唯一行的约定。使用PRIMARY KEY
(3个字节)而不是MEDIUMINT
(4个字节)可以节省每行额外的字节,更重要的是在INT
中每行节省2个字节 - 同时仍然可以提供超过1600万个字节日期和股票代码(每个)。
PRIMARY KEY
在此架构中,每对CREATE TABLE `prices` (
`market_day` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
`ticker_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
`price` decimal (7,2) NOT NULL DEFAULT '00000.00',
PRIMARY KEY USING BTREE (`market_day`,`ticker_id`),
KEY `ticker_id` USING BTREE (`ticker_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
;
和market_day
中的每一行都是唯一的。这里ticker_id
对应于ticker_id
表中的股票代码列表,其中包含与tickers
表类似的架构:
market_days
这产生了与其他人提出的类似查询,但有两个重要的区别:1)日期列上没有功能转换,这破坏了MySQL在连接上使用键的能力;在下面的查询中,MySQL将使用部分CREATE TABLE `tickers` (
`ticker_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`ticker_symbol` VARCHAR(5),
`company_name` VARCHAR(50),
/* etc */
PRIMARY KEY USING BTREE (`ticker_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0
;
加入PRIMARY KEY
。 2)MySQL每个market_day
或JOIN
子句只能使用一个密钥。在这个查询中,MySQL将使用WHERE
(PRIMARY KEY
和market_day
)的全宽,而在之前的查询中,它只能使用一个(MySQL通常会选择两个中更具选择性) )。
ticker_id
更精细的一点是,还需要加入SELECT
`market_days`.`date`,
`tickers`.`ticker_symbol`,
`yesterday`.`price` AS `close_yesterday`,
`today`.`price` AS `close_today`,
(`today`.`price` - `yesterday`.`price`) / (`yesterday`.`price`) AS `pct_change`
FROM
`prices` AS `today`
LEFT JOIN
`prices` AS `yesterday`
ON /* uses PRIMARY KEY */
`yesterday`.`market_day` = `today`.`market_day` - 1 /* this will join NULL for `today`.`market_day` = 0 */
AND
`yesterday`.`ticker_id` = `today`.`ticker_id`
INNER JOIN
`market_days` /* uses first 3 bytes of PRIMARY KEY */
ON
`market_days`.`market_day` = `today`.`market_day`
INNER JOIN
`tickers` /* uses KEY (`ticker_id`) */
ON
`tickers`.`ticker_id` = `today`.`ticker_id`
WHERE
`today`.`price` > 0
AND
`yesterday`.`price` > 0
;
和tickers
以显示实际的market_days
和ticker_symbol
,但这些操作非常快,因为它们很快利用钥匙。
答案 2 :(得分:2)
基本上,您可以将表连接到自身以查找给定的%更改。然后,按change
降序排序,以获得最大的变换器。如果你想要最大的波动,你甚至可以按abs(change)
订购。
select
p_today.ticker,
p_today.date,
p_yest.price as open,
p_today.price as close,
--Don't have to worry about 0 division here
(p_today.price - p_yest.price)/p_yest.price as change
from
prices p_today
inner join prices p_yest on
p_today.ticker = p_yest.ticker
and date(p_today.date) = date(date_add(p_yest.date interval 1 day))
and p_today.price > 0
and p_yest.price > 0
and date(p_today.date) = CURRENT_DATE
order by change desc
limit 10