我需要表示一些事件的位置,我正在为这个应用程序设计数据库模式. 我有两种表示位置的方法:

approach 1:

  • 国家/地区
  • 各州
  • 城市
  • 位置(在位置中,我有国家id、州id和城市id的外键)

approach 2:个 1表:

  • 位置,并且只有国家、州、城市字段,它们以文本形式存储(没有外来ID)

你推荐哪种方法?第一个将有助于消除可能的不同名称,例如同一国家(美国、美国、美国等).并且可能有助于在编写文本框时提供建议,这可能是强制性的.

然而,第二种方法似乎可以让一切变得更加简单,并且应该会减少对数据库的查询数量.

你认为哪一个更好?您知道这种情况下的最佳实践是什么吗?例如,它是如何做一些大型门户网站的,在这些门户网站上,他们也需要诸如位置之类的东西(例如,Foursquare等).AFAIK脸书使用第二种方法,但是.我想听听你的意见,以及你为什么会 Select 一种方法而不是另一种方法的可能原因.

谢谢!

推荐答案

Approach #1:

如果你想要满分normalized database分,这是个不错的解决方案.您可以轻松地管理所有表,但在查询位置时必须有3个左/内连接.我假设所有内容都被正确地编入索引,因此您在性能方面不会有真正的问题,因为这些表相对较小(国家和州)和中等大小的城市(如果您只想要特定国家的所有城市).如果您想要世界上所有的城市,那么这个表会很大,如果您没有正确地索引或连接表,那么在某个时候可能会出现性能问题.

由于所有内容都在数据库中,因此如果需要添加、更新或删除记录,则无需更改代码.

如果您需要添加、更新或删除任何记录,此解决方案将非常易于维护.如果您需要更新名称(例如,城市名称),则所有记录将同时更新.

如果按城市或州查找,查询将更快地运行,那么一个简单的左连接来获取名称就可以了.

Approach #2:

我个人不推荐这样做,因为对于可维护性而言,这不是最好的解决方案.如果有一天您需要检索基于城市的数据,如果您没有正确索引,查询的执行速度可能会很慢.如果您为国家、州、城市编制索引,那么查找速度会更快(但比第一种方法慢,因为varchar的索引速度比int慢).此外,您增加了名字出错的风险,例如:New York vs New york vs New Yrok.

此外,如果你需要更新一个城市的名称,你必须检索所有具有该名称的记录,然后更新所有这些记录.这可能需要很长时间.

例如:更新位置设置City=‘New York’where City=‘newyork’; *注意:另外,如果拼写错误,则必须验证所有记录以确保更新所有记录

Here's a skeleton based on your requirement (using MYSQL) for approach #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 |       |
+----+-------------+-----------+--------+----------------------------------------+---------+---------+-------+------+-------+

现在更新:

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

现在,如果我看一下我在那座城市的所有位置,所有人都会说:纽约

For approach #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 |
+-------------------+---------------+----------+---------------+

现在更新:

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

现在,如果我在我所有的地点寻找那个城市,并不是所有的地方都会说:New York

正如你所看到的,它花了1.29秒(是的,它很快),但是所有有"New York"的记录都被更新了,但是可能有一些拼写错误或名字不好等等……

Conclusion:个 仅出于这个原因,我宁愿采用第一种方法.

注: 国家和州很少改变.也许您可以在代码中包含这些内容,而不要从数据库中引用它们.这将从查询中保存2个内连接,在您的代码中,您只需检索国家或州的ID(如果需要创建HTML下拉框,情况也是如此).

此外,您还可以考虑使用memcached、APC、reddi或您喜欢的任何其他方式缓存这些国家和州.

Database相关问答推荐

使用 Npgsql 执行年龄查询并在 .NET 应用程序中返回结果?

是否可以同时从 RocksDB 读取?

为什么引用 SQLite rowid 会导致外键不匹配?

即使使用列入白名单的 IP,也无法连接到 Azure SQL 数据库

无法使用命令行运行 liquibase

类似 Hibernate 的 C++ 框架

数据库供应商如何实现事务?

库存数据库的最佳 struct

按最强 LIKE 排序 SQL?

将所有数据库列设置为 NOT NULL 是一种好习惯吗?

更改列类型而不丢失数据

mySQL 复制是否具有即时数据一致性?

归档实时 MySQL 数据库的最佳方式

哪个提供更好的性能一个大连接或多个查询?

在哪里可以找到 neo4j 快速教程?

如何使用 Realm 进行排序?

App=EntityFramework 在 Sql 连接字符串中有什么作用?

您是否应该将自引用表列设为外键?

SqlParameterCollection only accepts non-null SqlParameter type objects, not String objects

位图索引有何帮助?