Scott plantea un gran punto acerca de los días de mercado consecutivos. Yo recomiendo el manejo de este conector con una mesa como:
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
;
Como transcurren más días de mercado, sólo INSERT
nuevos date
valores de la tabla. market_day
se incrementará en consecuencia.
Al insertar datos de prices
, busque LAST_INSERT_ID()
o el valor correspondiente a date
para ver los valores pasados.
En cuanto a la tabla prices
en sí, se puede hacer de almacenamiento, y SELECT
INSERT
operaciones mucho más eficientes con un útil PRIMARY KEY
y ninguna columna AUTO_INCREMENT
. En el esquema a continuación, su PRIMARY KEY
contiene información intrínsecamente útil y no es solo una convención para identificar filas únicas. El uso de MEDIUMINT
(3 bytes) en lugar de INT
(4 bytes) guarda un byte adicional por fila y más importante aún 2 bytes por fila en el PRIMARY KEY
, todo mientras que ofrece más de 16 millones de fechas posibles y símbolos de cotización (cada uno).
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
;
En este esquema cada fila es único a través de cada par de market_day
y ticker_id
. Aquí ticker_id
corresponde a una lista de símbolos de cotización en una tabla tickers
con un esquema similar a la tabla 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
;
Esto produce una consulta similar a otras propuestas, pero con dos diferencias importantes: 1) No hay ninguna transformación funcional en la columna de fecha, que destruye la capacidad de MySQL para usar claves en la unión; en la consulta a continuación, MySQL usará parte del PRIMARY KEY
para unirse al market_day
. 2) MySQL solo puede usar una clave por JOIN
o WHERE
cláusula. En esta consulta MySQL usará el ancho completo de PRIMARY KEY
(market_day
y ticker_id
) mientras que en la consulta anterior solo podría usar uno (MySQL usualmente elegirá el más selectivo de los dos).
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
;
Un punto más fina es la necesidad de unirse también contra tickers
y market_days
con el fin de mostrar la real ticker_symbol
y date
, pero estas operaciones son muy rápidos ya que hacen uso de las teclas.
@Scott: Gracias por tu comentario. Puedo cambiar la fecha y hora a la fecha para facilitar las cosas en lugar de tener que lidiar con los rangos. –
@Knix La función de fecha es bastante limpia, no estoy seguro de lo caro que es, pero sin duda su llamada. Todavía hay un problema sobre el cierre del mercado los fines de semana y días festivos. columna previousClose elimina la unión automática, el desorden de los días de mercado cerrados a expensas de duplicar datos y tener que saber el cierre anterior al insertar el cierre de hoy. – Scott
Gracias ... ¡Tomaré su consejo! –