Approach # 1:
Đây là một giải pháp tốt nếu bạn muốn có một tốt normalized database. Bạn có thể quản lý tất cả các bảng của bạn một cách dễ dàng nhưng bạn sẽ phải có 3 trái/bên trong tham gia khi bạn truy vấn vị trí. Tôi cho rằng mọi thứ được lập chỉ mục đúng cách, do đó bạn sẽ không gặp vấn đề gì với hiệu suất vì các bảng này sẽ tương đối nhỏ (quốc gia và tiểu bang) và kích thước trung bình cho Thành phố (nếu bạn chỉ muốn tất cả các thành phố cho một quốc gia cụ thể). Nếu bạn muốn TẤT CẢ các thành phố trên thế giới mà bảng sẽ rất lớn và bạn có thể có vấn đề hiệu suất tại một số điểm nếu bạn không lập chỉ mục hoặc tham gia bảng một cách chính xác.
Vì mọi thứ đều có trong cơ sở dữ liệu, bạn không phải thay đổi mã nếu bạn cần thêm, cập nhật hoặc xóa bản ghi.
Nếu bạn cần thêm, cập nhật hoặc xóa bất kỳ bản ghi nào, giải pháp này sẽ rất dễ bảo trì. Nếu bạn cần cập nhật tên (ví dụ tên thành phố) và tất cả các bản ghi sẽ được cập nhật cùng một lúc.
Truy vấn sẽ chạy nhanh hơn nếu bạn nhìn theo thành phố hoặc tiểu bang sẽ nhanh chóng, sau đó việc tham gia trái đơn giản để lấy tên sẽ thực hiện thủ thuật.
Cách tiếp cận # 2:
Cá nhân tôi sẽ không khuyên bạn điều này vì cho bảo trì nó không phải là giải pháp tốt nhất. Nếu một ngày nào đó bạn cần truy xuất dữ liệu dựa trên thành phố, truy vấn của bạn có thể chậm để thực thi nếu bạn không lập chỉ mục đúng cách. Nếu bạn lập chỉ mục quốc gia, tiểu bang, thành phố thì sẽ nhanh hơn để tra cứu (nhưng chậm hơn so với phương pháp đầu tiên vì varchar chậm hơn int để lập chỉ mục). Ngoài ra, bạn tăng nguy cơ lỗi cho các tên ví dụ: New York VS newyork VS New Yrok.
Ngoài ra, nếu bạn cần cập nhật tên thành phố, bạn sẽ phải truy xuất tất cả các bản ghi có tên đó rồi cập nhật tất cả các bản ghi này. Có thể mất nhiều thời gian.
ví dụ: UPDATE vị trí SET city = 'New York' nơi city = 'newyork'; * lưu ý: còn nếu bạn có viết sai, bạn sẽ phải xác nhận tất cả hồ sơ để đảm bảo bạn cập nhật tất cả hồ sơ
Dưới đây là một bộ xương dựa trên yêu cầu của bạn (sử dụng MYSQL) cho phương pháp # 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 | |
+----+-------------+-----------+--------+----------------------------------------+---------+---------+-------+------+-------+
Bây giờ cập nhật:
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
Bây giờ nếu tôi nhìn tất cả các vị trí của tôi cho thành phố đó, tất cả sẽ nói: New York
Đối với phương pháp # 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 |
+-------------------+---------------+----------+---------------+
Bây giờ cập nhật:
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
Bây giờ nếu tôi nhìn tất cả các vị trí của tôi cho thành phố đó, không phải tất cả sẽ nói: New York
Như bạn có thể thấy, nó mất 1,29 giây (có nó nhanh) nhưng tất cả hồ sơ có "New York" đã được cập nhật nhưng có thể có một số lỗi chính tả hoặc tên xấu, v.v ...
Kết luận: Vì lý do này, tôi thay vì đi theo cách tiếp cận đầu tiên.
Lưu ý: Quốc gia và các quốc gia hiếm khi thay đổi. Có lẽ bạn có thể có chúng trong mã của bạn và không tham khảo chúng từ cơ sở dữ liệu. Điều này sẽ lưu 2 INNER JOIN từ truy vấn và chúng trong mã của bạn, bạn chỉ cần truy xuất ID của quốc gia hoặc tiểu bang (cùng một điều nếu bạn cần tạo một hộp thả xuống HTML).
Ngoài ra, bạn có thể nghĩ về việc lưu vào bộ nhớ đệm các quốc gia và tiểu bang này bằng cách sử dụng như memcached, APC, reddis hoặc bất kỳ ứng dụng nào khác mà bạn thích.
bất kỳ đề xuất nào? – Bart
Động cơ nào? MySQL? Oracle? DB9? SqlLite? –
Liệu nó có quan trọng không? Nếu có, MySQL, nhưng nếu bạn có thể chỉ ra điều gì sẽ là sự khác biệt trong trường hợp ví dụ: Oracle, điều đó cũng hữu ích ... – Bart