In via di introduzione ...
Ho eseguito in questa domanda: Difference between 2 adjacent fields - Date - PHP MYSQL e stava cercando di raggiungere l'obiettivo, vale a dire scorrere le date e ottenere diff, con puro MySQL.
Un'altra domanda (Subtracting one row of data from another in SQL) mi ha aiutato a capire come creare qualcosa di simile con MySQL. Non ha risolto il problema, poiché le soluzioni sono ancora depandant sia su valori fissi che sull'ordine assunto dei dati, ma mi ha aiutato a capire la metodologia.
C'è un'altra domanda (How to get next/previous record in MySQL?) con le risposte che descrivono come ottenere i valori dalla riga successiva/precedente. Dipende ancora da alcuni valori fissi, ma ho imparato a usare la tecnica.data di MySQL Query diff iterazione - razionalizzare query o ottimizzare la struttura dei dati
Dire che ho questa tabella 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
ci sono due ipotesi:
- Chiave primaria (
id
) ordinato ascendente e "buchi" consentiti. - Ogni data nella colonna
dateof
è valida, nel significato: noNULL
s e senza valori predefiniti (0000-00-00
). Voglio scorrere ogni riga e calcolare il numero di giorni trascorsi con la voce precedente, di ricevere questo:
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 tutto quello che ho imparato sono arrivato a questa soluzione (diciamo soluzione 1 , in quanto non v'è un altro):
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;
(esempio violino qui: http://sqlfiddle.com/#!2/099fc/3).
Questo funziona come un incantesimo ... finché non ci sono solo un paio di voci in db. C'è di peggio quando più:
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 righe: Durata: 8,672 sec. Fetch: 228.515 sec.
ho pensato di aggiungere indice su dateof
colonna:
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;
... ed ha guadagnato piccolo miglioramento:
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 righe: Durata: 8,406 sec . Fetch: 219.281 sec.
Ho ricordato di aver letto da qualche parte sui vantaggi di MyISAM su InnoDB, in alcuni casi.Così ho cambiato la a MyISAM:
ALTER TABLE `foo` ENGINE = MyISAM;
18031 Righe: durata: 5,671 sec. Fetch: 151.610 sec.
Certo che è meglio ma ancora lento.
Ho provato con un altro algoritmo (soluzione 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;
... ma era ancora più lenta:
18031 righe: durata: 15,609 sec. Recupero: 184,656 sec.
Ci sono altri modi per ottimizzare la query o struttura di dati al fine di avere questo compito eseguito più velocemente?
Penso che una diversa struttura di dati possa essere più appropriata alle vostre esigenze. Puoi dire qualcosa in più su come stai cercando di usare questi dati? – eggyal
@eggyal Niente di particolare. Sto solo cercando di imparare qualcosa che potrebbe essere utile :) – bostaf