2011-07-11 4 views
18

私はいくつかのイベントの場所を表す必要があり、私はこのアプリケーションのデータベーススキーマを設計しています。 私は場所を提示するための二つのアプローチがあります。データベーススキーマ - 位置を表す

アプローチ1: 4つのテーブル:

  • 米国
  • 都市
  • 場所(場所で、私はへの外部キーを持っているがcountry_id、state_id、city_id)

アプローチ2: 1表:

  • ロケーションと単にテキストとして国、州、市が保存されるフィールドです(無外国のidの)

あなたはどちらのアプローチをお勧めしますか?最初のものは可能性のある異なる名前を排除するのに役立ちます。同じ国(アメリカ、米国、米国など)となり、おそらく必須となるテキストボックスで書くときに提案を提供するのに役立ちます。

しかし、2つ目の方法は、すべてをより簡単に保つことができ、データベースへのクエリ数を減らす必要があるようです。

あなたはどちらが良いと思いますか?この場合のベストプラクティスは何ですか?例えば。場所(例:正方形など)が必要な大きなポータルはどうでしたか? Afaikのfacebookは2番目のアプローチを使っていますが、私はあなたの意見を聞きたいのではないでしょうか。

ありがとうございます!

+0

提案がありますか? – Bart

+0

どのエンジン? MySQL?オラクル? DB9? SqlLite? –

+0

それは重要な問題でしょうか?もしそうならば、MySQLは、しかし、あなたが何の違いがあるかを指摘することができれば。オラクル、それはあまりにも役に立つかもしれません... – Bart

答えて

17

アプローチ#1:あなたが良いnormalized databaseをしたい場合

これは良い解決策です。あなたは簡単にすべてのテーブルを管理することができますが、あなたは場所を照会するときに3つの左/内の結合が必要です。私はすべてが適切に索引付けされていると仮定しているため、実際にはパフォーマンスに問題はありません。これらの表は都市の比較的小さい(国と州)と中規模です(特定の国のすべての都市のみ)。世界のすべての都市がテーブルを巨大にしたい場合は、テーブルを正しく索引付けしたり結合したりしないと、ある時点でパフォーマンスの問題が発生する可能性があります。

レコードはすべて追加されているため、レコードの追加、更新、または削除が必要な場合は、コードを変更する必要はありません。

レコードを追加、更新、または削除する必要がある場合は、このソリューションは非常に簡単に維持できます。名前(都市名など)を更新する必要がある場合は、すべてのレコードが一度に更新されます。

都市や州を見ていると検索が高速になり、名前を取得するための簡単な左の結合がそのトリックを行います。

アプローチ#2:

保守のためにそれが最善の解決策ではありませんので、私は個人的にこれをお勧めしません。都市に基づいてデータを取得する必要がある場合は、適切にインデックスを作成しないとクエリの実行が遅くなることがあります。国、州、市のインデックスを作成すると、ルックアップが高速になります(しかし、varcharはインデックス作成のためにintより遅いため、最初のアプローチよりも遅くなります)。また、名前の誤りのリスクを増やすこともできます。例えば、New York VS newyork VS New Yrok。

また、都市の名前を更新する必要がある場合は、その名前を持つすべてのレコードを取得し、これらのレコードをすべて更新する必要があります。それは長い時間がかかることがあります。

例:UPDATE locations SET city = 'New York'ここでcity = 'newyork'; *注:また、あなたがmisspellsを持っている場合、あなたはすべてのレコード

を更新を確認するためにすべてのレコードを検証する必要があります。ここのアプローチ#1の(MYSQLを使用して)あなたの条件に基づいて、スケルトンの:

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 

私はその都市のすべての私の場所を見れば今、すべては言うだろう:ニューヨーク

アプローチ#2の3210

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 

を今、私はその都市のすべての私の場所を見れば、すべてではないと言うだろう:

ニューヨークご覧のとおり、1.29秒かかりました(高速です)。しかし、 "New York"を含むすべてのレコードが更新されましたが、スペルミスや悪い名前などがあります。

結論: この理由だけで、私はむしろ最初のアプローチに行きます。

注: 国と状態はほとんど変わりません。たぶん、あなたのコードでこれらを持つことができ、それらをデータベースから参照しないでください。これにより、クエリーからの2つのINNER JOINが保存され、コード内では国または州のIDを取得するだけです(HTMLドロップダウンボックスを作成する必要がある場合は同じことです)。

また、memcached、APC、reddisなどの好きな国や州をキャッシュすることも考えられます。

4

#1、#2は問題を引き起こす可能性がある正規化されていません。

関連する問題