如何使用MySQL计算每日最高价格变化百分比?

时间:2009-08-09 04:56:40

标签: sql mysql

我有一张名为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计算?

非常感谢任何建议。

3 个答案:

答案 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列更有效地进行存储,INSERTPRIMARY 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_dayJOIN子句只能使用一个密钥。在这个查询中,MySQL将使用WHEREPRIMARY KEYmarket_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_daysticker_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