A modo de introducción ...
He encontrado esta pregunta: Difference between 2 adjacent fields - Date - PHP MYSQL y estaba tratando de lograr el objetivo, es decir, repetir fechas y obtener diff, con puro MySQL.
Otra pregunta (Subtracting one row of data from another in SQL) me ayudó a entender cómo hacer algo similar con MySQL. No resolvió el problema, ya que las soluciones siguen siendo depandant en valores fijos o en el orden asumido de datos, pero me ayudó a entender la metodología.
Hay otra pregunta (How to get next/previous record in MySQL?) con respuestas que describen cómo obtener valores de la fila siguiente/anterior. Todavía depende y en algunos valores fijos, pero aprendí a usar la técnica.MySQL date diff iteration query - optimizar la consulta u optimizar la estructura de datos
Decir que tengo esta tabla foo
:
CREATE TABLE `foo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dateof` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
id | dateof
-----+------------
1 | 2012-01-01
2 | 2012-01-02
11 | 2012-01-04
12 | 2012-01-01
13 | 2012-01-02
14 | 2012-01-09
111 | 2012-01-01
112 | 2012-01-01
113 | 2012-01-01
Hay dos supuestos:
- clave primaria (
id
) ordenó ascendente y "agujeros" permitidas. - Cada fecha en la columna
dateof
es válida, en el significado: noNULL
sy no hay valores predeterminados (0000-00-00
). Quiero recorrer cada fila y calcular el número de días transcurridos con la entrada anterior, para recibir este:
id | date | days_diff
-----+------------+-----------
1 | 2012-01-01 | 0
2 | 2012-01-02 | 1
11 | 2012-01-04 | 2
12 | 2012-01-01 | -3
13 | 2012-01-02 | 1
14 | 2012-01-09 | 7
111 | 2012-01-01 | -8
112 | 2012-01-01 | 0
113 | 2012-01-01 | 30
Con todo lo que he aprendido que vine a esta solución (por ejemplo solución al 1 , ya que hay otro):
SELECT
f.id,
DATE_FORMAT(f.dateof, '%b %e, %Y') AS date,
(SELECT DATEDIFF(f.dateof, f2.dateof)
FROM foo f2
WHERE f2.id = (
SELECT MAX(f3.id) FROM foo f3 WHERE f3.id < f.id
)
) AS days_diff
FROM foo f;
(violín ejemplo aquí: http://sqlfiddle.com/#!2/099fc/3).
Esto funciona como un encanto ... hasta que haya solo un par de entradas en db. Se pone peor cuando hay más:
EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY f ALL NULL NULL NULL NULL 17221
2 DEPENDENT SUBQUERY f2 eq_ref PRIMARY PRIMARY 4 func 1 Using where
3 DEPENDENT SUBQUERY f3 index PRIMARY PRIMARY 4 NULL 17221 Using where; Using index
18031 filas: duración: 8.672 seg. Buscar: 228.515 seg.
pensé en añadir índice en la columna dateof
:
CREATE TABLE `foo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dateof` date DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `dateof` (`dateof`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
... y ganaron pequeña mejora:
EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY f index NULL dateof 4 NULL 18369 Using index
2 DEPENDENT SUBQUERY f2 eq_ref PRIMARY PRIMARY 4 func 1 Using where
3 DEPENDENT SUBQUERY f3 index PRIMARY dateof 4 NULL 18369 Using where; Using index
18031 filas: duración: 8.406 seg . Buscar: 219.281 seg.
Recordé leer en algún lugar acerca de las ventajas de MyISAM sobre InnoDB, en algunos casos.Así que cambié el que MyISAM:
ALTER TABLE `foo` ENGINE = MyISAM;
18031 filas: duración: 5.671 seg. Buscar: 151.610 seg.
Claro que es mejor, pero aún lento.
he intentado con otro algoritmo (solución al 2):
SELECT
f.id,
DATE_FORMAT(f.dateof, '%b %e, %Y') AS date,
(SELECT DATEDIFF(f.dateof, f2.dateof)
FROM foo f2
WHERE f2.id < f.id
ORDER BY f2.id DESC
LIMIT 1
) AS days_diff
FROM foo f;
... pero era aún más lento:
18031 filas: duración: 15.609 seg. Buscar: 184.656 seg.
¿Hay otras maneras de optimizar esta consulta o estructura de datos con el fin de que esta tarea sea realizada más rápido?
Creo que una estructura de datos diferente puede ser más apropiada para sus necesidades. ¿Puedes decir un poco más sobre cómo estás tratando de usar esta información? – eggyal
@eggyal Nada en particular. Estoy tratando de aprender algo que puede ser útil :) – bostaf