Скотт поднимает замечательный вопрос о последовательных рыночных днях. Я рекомендую обрабатывать это с помощью таблицы соединителей, например:
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()
или соответствующее значение для данного date
для прошлых значений.
Что касается самой таблицы prices
, вы можете сделать хранилище, операции SELECT
и INSERT
гораздо более эффективными с полезным столбцом PRIMARY KEY
и без AUTO_INCREMENT
. В приведенной ниже схеме ваш PRIMARY KEY
содержит полезную информацию, а не просто соглашение по идентификации уникальных строк. Использование MEDIUMINT
(3 байта) вместо INT
(4 байта) экономит дополнительный байт на строку и, что еще важнее, 2 байта на строку в PRIMARY KEY
- при этом все еще предоставляется более 16 миллионов возможных дат и символов тикера (каждый ).
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
:
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
;
Это дает запрос, аналогичный другим предложенным, но с двумя важными отличиями: 1) В столбце даты нет функционального преобразования, которое разрушает способность MySQL использовать ключи в соединении; в приведенном ниже запросе MySQL будет использовать часть PRIMARY KEY
, чтобы присоединиться к market_day
. 2) MySQL может использовать только один ключ в предложении JOIN
или WHERE
. В этом запросе MySQL будет использовать полную ширину PRIMARY KEY
(market_day
и ticker_id
), тогда как в предыдущем запросе он мог использовать только один (MySQL обычно выбирает более селективный из двух).
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
и date
, но эти операции выполняются очень быстро, поскольку они используют ключи.