Dovendo realizzare una nuova versione di un importante sito di aste per beneficenza mi sono ritrovato a dover recuperare i dati degli utenti e delle associazioni ONLUS presenti nel database in uso reinserendoli nel database realizzato da me. Ovviamente, non si è trattato di copiare le vecchie tabelle nel nuovo database, sarebbe stato troppo semplice e questo articolo sarebbe stato pressoché inutile. Quello che invece ho dovuto realizzare sono stati una serie di script, in SQL e PHP, per recuperare questi dati. Un’occasione per rispolverare il mio SQL arrugginito.
Tabelle di partenza:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
CREATE TABLE IF NOT EXISTS `addfield_data` ( `id` int(11) NOT NULL AUTO_INCREMENT, `codlabel` int(11) NOT NULL DEFAULT '0', `codelement` int(11) NOT NULL DEFAULT '0', `valore` text, `valoreint` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8293 ; CREATE TABLE IF NOT EXISTS `addfield_items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `codlang` int(11) NOT NULL DEFAULT '0', `etichetta` varchar(255) NOT NULL DEFAULT '', `required` int(11) NOT NULL DEFAULT '0', `default_value` longtext, `module` varchar(100) NOT NULL DEFAULT 'users', `module_action` varchar(40) NOT NULL DEFAULT 'new', `input_type` varchar(50) NOT NULL DEFAULT 'text', `public` int(11) NOT NULL DEFAULT '1', `etichetta_leggibile` varchar(250) DEFAULT NULL, `ordinamento` int(11) NOT NULL DEFAULT '1', `escluso_dal_gruppo` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=113 ; CREATE TABLE IF NOT EXISTS `utenti` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `nome` varchar(255) DEFAULT NULL, `cognome` varchar(255) DEFAULT NULL, `datareg` varchar(45) DEFAULT NULL, `lastlogin` varchar(45) DEFAULT NULL, `temp` int(10) unsigned NOT NULL DEFAULT '0', `banned` int(10) unsigned NOT NULL DEFAULT '0', `newsletter` int(10) unsigned NOT NULL DEFAULT '0', `n_post` int(10) unsigned NOT NULL DEFAULT '0', `active` int(10) unsigned NOT NULL DEFAULT '1', `sex` int(10) unsigned NOT NULL DEFAULT '0', `email` varchar(255) NOT NULL DEFAULT '', `expiry` varchar(40) NOT NULL DEFAULT 'never', `avatar` varchar(255) DEFAULT NULL, `md5_check` varchar(50) DEFAULT NULL, `file1` varchar(250) DEFAULT NULL, `file2` varchar(250) DEFAULT NULL, `file3` varchar(250) DEFAULT NULL, `file4` varchar(250) DEFAULT NULL, `offerta_1` int(20) DEFAULT NULL, `offerta_2` int(20) DEFAULT NULL, `descr_offerta_1` text, `descr_offerta_2` text, `data_nascita` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=478 ; |
Partiamo dall’inizio. Il database di partenza, parliamo di MySQL, come detto, prevedeva una tabella principale contenente sia dati utenti sia associazioni e due tabelle secondarie con ulteriori dati sempre in comune discriminati dal numero ID di una delle due tabelle e da un numero di riferimento. In soldoni, spero di essere abbastanza chiaro, una delle due tabelle secondarie indicava il contenuto dei valori aggiuntivi nell’altra tabella secondaria. Sicuramente una soluzione ricavata dall’adattamento di un software ed un database non realizzato per questo scopo. Nella mia nuova versione volevo rendere la struttura del database un po’ più professionale e funzionale ma più semplice, con solo due diverse tabelle, una per gli utenti e una per le associazioni.
Tabelle di destinazione:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
CREATE TABLE IF NOT EXISTS `associazioni` ( `id` int(11) NOT NULL AUTO_INCREMENT, `denominazione` varchar(50) DEFAULT NULL, `acronimo` tinytext, `cf` tinytext NOT NULL, `iva` tinytext NOT NULL, `iban` varchar(50) NOT NULL, `annocostituzione` varchar(4) NOT NULL, `web` varchar(50) NOT NULL, `email` tinytext NOT NULL, `indirizzo` tinytext NOT NULL, `cap` varchar(5) NOT NULL, `citta` varchar(50) NOT NULL, `regione` varchar(20) NOT NULL, `provincia` varchar(20) NOT NULL, `nazione` varchar(20) NOT NULL, `telefono` tinytext NOT NULL, `fax` tinytext NOT NULL, `sedelegalediversa` int(1) NOT NULL, `indirizzosedeoperativa` tinytext, `capsedeoperativa` tinytext, `cittasedeoperativa` tinytext, `regionesedeoperativa` tinytext, `provinciasedeoperativa` tinytext, `telefonosedeoperativa` tinytext, `faxsedeoperativa` tinytext, `nomelegalerappresentante` tinytext NOT NULL, `cognomelegalerappresentante` tinytext NOT NULL, `codicefiscalelegalerappresentante` tinytext NOT NULL, `emaillegalerappresentante` varchar(50) NOT NULL, `nomeriferimento` tinytext NOT NULL, `cognomeriferimento` tinytext NOT NULL, `emailriferimento` tinytext NOT NULL, `telefonoriferimento` varchar(20) NOT NULL, `ruoloriferimento` tinytext NOT NULL, `nomeresponsabilecomunicazione` tinytext, `cognomeresponsabilecomunicazione` tinytext, `emailresponsabilecomunicazione` tinytext, `settoreattivita` tinytext NOT NULL, `areageografica` tinytext NOT NULL, `descrizione` text NOT NULL, `logo` tinytext NOT NULL, `progetto` tinytext, `attocostitutivo` tinytext NOT NULL, `statuto` tinytext NOT NULL, `anagrafe` tinytext NOT NULL, `prezzopacchetto1` varchar(10) NOT NULL, `descrizionepacchetto1` tinytext NOT NULL, `prezzopacchetto2` varchar(10) NOT NULL, `descrizionepacchetto2` tinytext NOT NULL, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `hash` varchar(5) NOT NULL, `suspended` int(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=524 ; CREATE TABLE IF NOT EXISTS `users` ( `id` int(32) NOT NULL AUTO_INCREMENT, `nick` varchar(20) NOT NULL, `password` varchar(32) DEFAULT '', `hash` varchar(5) DEFAULT '', `name` tinytext, `lastname` tinytext, `address` tinytext, `city` varchar(25) DEFAULT '', `prov` varchar(20) DEFAULT '', `country` varchar(30) DEFAULT '', `zip` varchar(10) DEFAULT '', `phone` varchar(40) DEFAULT '', `email` varchar(50) DEFAULT '', `reg_date` int(15) DEFAULT NULL, `rate_sum` int(11) NOT NULL DEFAULT '0', `rate_num` int(11) NOT NULL DEFAULT '0', `birthdate` int(8) DEFAULT '0', `suspended` int(1) DEFAULT '0', `nletter` int(1) NOT NULL DEFAULT '0', `balance` double NOT NULL DEFAULT '0', `auc_watch` text, `item_watch` text, `endemailmode` enum('one','cum','none') NOT NULL DEFAULT 'one', `startemailmode` enum('yes','no') NOT NULL DEFAULT 'yes', `emailtype` enum('html','text') NOT NULL DEFAULT 'html', `lastlogin` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `payment_details` text, `groups` text, `bn_only` enum('y','n') NOT NULL DEFAULT 'y', `timecorrection` int(3) NOT NULL DEFAULT '0', `language` char(2) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=474 ; |
Ovviamente non posso pubblicare un dump per ovvi motivi di privacy.
Per complicare un po’ le cose, i campi da una tabella all’altra non sono ovviamente gli stessi, anzi, alcuni non servono più mentre ce ne sono dei nuovi. Inoltre le password erano codificate in Base64, una forma di codifica reversibile assolutamente non adatta a questo scopo. Insomma, vediamo cosa ho fatto…
Allora, per prima cosa recuperiamo i dati in comune prima per gli utenti e poi per le associazioni. Uno script SQL opportunamente modificato per le due situazioni, utenti e associazioni, permetterà di recuperare questi dati. Fortunatamente come discriminante tra i dati utenti e quelli delle associazioni vi è il campo della data di nascita, vuoto per associazioni e pieno per gli utenti:
|
1 2 3 4 5 |
INSERT INTO `db-test`.`associazioni` (`id`,`denominazione`,`acronimo`,`logo`,`attocostitutivo`,`statuto`,`anagrafe`,`email`,`prezzopacchetto1`,`descrizionepacchetto1`,`prezzopacchetto2`,`descrizionepacchetto2`,`username`,`password`) SELECT id,nome,cognome,avatar,file1,file2,file3,email,offerta_1,descr_offerta_1,offerta_2,descr_offerta_2,username,password FROM `db-old`.`utenti` WHERE `data_nascita` IS NULL |
Cambiando i nomi della tabella (nel codice vedete lo script per le “associazioni”) e dei campi si può usare lo stesso script per travasare un ulteriore serie di dati in una seconda tabella, quella degli utenti. In questo modo abbiamo recuperato una serie di dati da una tabella del vecchio database a due tabelle del nuovo database.
Fin qui nessuna apparente difficoltà, adesso però dobbiamo recuperare le informazioni dalle due tabelle secondarie e qui la cosa si complica leggermente. Come dicevo, una tabella riporta una serie di valori di riferimento mentre nella seconda tabella secondaria vi sono questi valori, il tutto legato dagli ID.
Ecco quindi lo script che confronta il numero ID delle informazioni già recuperate con quello di una delle tabelle e recupera poi la relativa informazione:
|
1 2 3 4 5 6 7 |
UPDATE `db-test`.`associazioni` SET `areageografica` = /* <-- modificare per ogni colonna da aggiornare */ (SELECT `valore` FROM `db-old`.`addfield_data` WHERE `db-test`.`associazioni`.`id` = `db-old`.`addfield_data`.`codelement` AND `db-old`.`addfield_data`.`codlabel` = 79 /* <-- modificare per ogni colonna da aggiornare */ ); |
Anche in questo caso, cambiando il nomi delle tabelle possiamo recuperare sia i dati utente che i dati associazioni.
A questo punto cambiamo le password da Base64 a MD5(salt+password), pertanto devo rimettere in chiaro le password e ricodificarle:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php //cambio password, da base64 a MD5+salt $MD5_PREFIX = "[codice casuale di 128 caratteri]"; $connect = mysql_connect("localhost", "root", "password") or die("Couldn't connect"); mysql_select_db("db-test") or die("Couldn't find the database"); $query = mysql_query("SELECT * FROM associazioni"); $numrows = mysql_num_rows($query); if ($numrows != 0) { while ($row = mysql_fetch_assoc($query)) { $clear = base64_decode($row['password']); $password = md5($MD5_PREFIX . $clear); $substitute = mysql_query("UPDATE associazioni SET password = '$password' WHERE id = '$row[id]'"); if(! $substitute ) { die('Could not update data: ' . mysql_error()); } echo "Password in chiaro: " . $clear . str_repeat(' ', 10) . "Password codificata: " . $password . "<br>"; } } ?> |
Ovviamente, per essere sicuro del risultato ho voluto fare qualche test prima ancora di utilizzare in modo definitivo i dati migrati. Ecco quindi un paio di script scritti apposta per la password.
Il primo script rimette la password originale:
|
1 2 3 4 5 6 |
//recupero password originale UPDATE `db-test`.`associazioni` SET `password` = (SELECT password FROM `db-old`.`utenti` WHERE `db-test`.`associazioni`.`id` = `db-old`.`utenti`.`id` AND `data_nascita` IS NULL) |
Questo secondo script invece confronta una delle password con la nuova codifica per verificare se la conversione è riuscita:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php //controllo se la password corrisponde $connect = mysql_connect("localhost", "root", "password") or die("Couldn't connect"); mysql_select_db("db-test") or die("Couldn't find the database"); $query = mysql_query("SELECT * FROM associazioni"); while ($row = mysql_fetch_assoc($query)) { if ($row['password'] == md5($row['hash'].'password')) { echo "OK!"; } } ?> |
Ecco quindi la cronaca di una giornata di lavoro passata solo a recuperare dati. Una libidine.

.gif)