Enfoque # 1:
Ésta es una buena solución si quieres un buen normalized database. Puede administrar todas sus tablas fácilmente, pero deberá tener 3 combinaciones izquierda/interna cuando consulte la ubicación. Supongo que todo está indexado correctamente para que no tenga problemas con el rendimiento, ya que estas tablas serán relativamente pequeñas (país y estados) y de tamaño mediano para Ciudades (si solo desea todas las ciudades para un país específico solamente). Si desea TODAS las ciudades del mundo, esa tabla será enorme y podría tener problemas de rendimiento en algún momento si no indexa o no se une a la tabla correctamente.
Como todo está en la base de datos, no tiene que cambiar el código si necesita agregar, actualizar o eliminar un registro.
Si necesita agregar, actualizar o eliminar cualquier registro, esta solución será muy fácil de mantener. Si necesita actualizar un nombre (por ejemplo, el nombre de la ciudad), todos los registros se actualizarán a la vez.
Las consultas serán más rápidas de ejecutar si mira por ciudad o estado será rápido, entonces una simple combinación de la izquierda para obtener el nombre hará el truco.
Enfoque # 2:
personalmente no recomiendo esto porque para el mantenimiento no es la mejor solución. Si algún día necesita recuperar datos basados en una ciudad, su consulta puede ejecutarse lentamente si no se indexa correctamente. Si indexa el país, estado, ciudad, entonces será más rápido para la búsqueda (pero más lento que el primer enfoque, ya que varchar es más lento que int para la indexación). Además, aumenta el riesgo de errores para los nombres, por ejemplo: New York VS newyork VS New Yrok.
Además, si necesita actualizar el nombre de una ciudad, tendrá que volver a recuperar todos los registros que tengan ese nombre y luego actualizar todos estos registros. Que puede llevar mucho tiempo.
por ejemplo: UPDATE ubicaciones SET city = 'Nueva York' donde city = 'newyork'; * Nota: También si tiene Erratas, que tendrá que validar todos los registros para asegurarse de actualizar todos los registros
Aquí es un esqueleto en base a sus necesidades (el uso de MySQL) para la aproximación # 1:
CREATE TABLE `countries` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `states` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL DEFAULT '',
`fk_country_id` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `fk_country_id` (`fk_country_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `cities` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL DEFAULT '',
`fk_state_id` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `fk_state_id` (`fk_state_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `locations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL DEFAULT '',
`fk_country_id` int(10) NOT NULL DEFAULT '0',
`fk_state_id` int(10) NOT NULL DEFAULT '0',
`fk_cities_id` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `fk_country_id` (`fk_country_id`),
KEY `fk_state_id` (`fk_state_id`),
KEY `fk_cities_id` (`fk_state_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/* This table should not have fk_country_id and fk_state_id since they are already in their respective tables. but for this requirement I will not remove them from the table */
SELECT locations.name AS location, cities.name AS city, states.name AS state, countries.name AS country from locations INNER JOIN cities ON (cities.id = fk_cities_id) INNER JOIN states ON (states.id = locations.fk_state_id) INNER JOIN countries ON (countries.id = locations.fk_country_id);
+-------------------+---------------+----------+---------------+
| location | cty | state | country |
+-------------------+---------------+----------+---------------+
| Statue of Liberty | New York City | New York | United States |
+-------------------+---------------+----------+---------------+
1 row in set (0.00 sec)
EXPLAIN:
+----+-------------+-----------+--------+----------------------------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+--------+----------------------------------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | locations | system | fk_country_id,fk_state_id,fk_cities_id | NULL | NULL | NULL | 7174 | |
| 1 | SIMPLE | cities | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | states | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | countries | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+-----------+--------+----------------------------------------+---------+---------+-------+------+-------+
Ahora Actualización:
UPDATE states SET name = 'New York' WHERE ID = 1; //using the primary for update - we only have 1 New York City record in the DB
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Ahora si miro todos mis ubicaciones para esa ciudad, todos dirán: Nueva York
Para la aproximación # 2:
CREATE TABLE `locations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL DEFAULT '',
`fk_country_id` varchar(200) NOT NULL default '',
`fk_state_id` varchar(200) NOT NULL default '',
`fk_cities_id` varchar(200) NOT NULL default '',
PRIMARY KEY (`id`),
KEY `fk_country_id` (`fk_country_id`),
KEY `fk_state_id` (`fk_state_id`),
KEY `fk_cities_id` (`fk_state_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
SELECT location, city, state, country FROM locations;
+-------------------+---------------+----------+---------------+
| location | city | state | country |
+-------------------+---------------+----------+---------------+
| Statue of Liberty | New York City | New York | United States |
+-------------------+---------------+----------+---------------+
Ahora Actualización:
UPDATE locations SET name = 'New York' WHERE name = 'New York City'; // can't use the primary key for update since they are varchars
Query OK, 0 rows affected (1.29 sec)
Rows matched: 151 Changed: 151 Warnings: 0
Ahora si miro todos mis ubicaciones para esa ciudad, no todos van a decir: Nueva York
Como puede ver, demoraron 1.29 segundos (sí, es rápido) pero todos los registros que tienen "Nueva York" se actualizaron, pero tal vez haya errores de ortografía o malos nombres, etc. ...
Conclusión: Por esta razón, prefiero seguir con el primer enfoque.
Nota: El país y los Estados rara vez cambian. Tal vez pueda tenerlos en su código y no hacer referencia a ellos desde la base de datos. Esto ahorrará 2 INNER JOIN de la consulta y ellos en tu código. Simplemente recuperas el ID del país o estado (lo mismo si necesitas crear un cuadro desplegable HTML).
Además, puede pensar en almacenar en caché estos países y estados utilizando memcached, APC, reddis o cualquier otro que prefiera.
alguna sugerencia? – Bart
¿Qué motor? MySQL? ¿Oráculo? DB9? SqlLite? –
¿Importaría significativamente? Si es así, MySQL, pero si pudieras señalar cuál sería la diferencia en caso de, p. Oracle, eso también podría ser útil ... – Bart