私の会社では、さまざまな地域でサービスを販売するモバイルアプリを開発します。ユーザーがSearch関数に大きく依存することを期待しています。したがって、データベースの設計が検索を処理するのに十分なものであることを確認する必要があります。
私の主な懸念は、ユーザーが名前や説明でサービスを検索できるだけでなく、サービスの場所でも検索できるようにすることです。
まず、私が設計したものを紹介しましょう。
Solution 1
Table: Service
+------+----------+--------------+--------+----------+----------+
| id | name | description | cityid | address1 | address2 |
+------+----------+--------------+--------+----------+----------+
| 1| Service1 | Description1 | 1| Address1 | |
| 2| Service2 | Description2 | 2| Address2 | |
| 3| Service3 | Description3 | 3| Address3 | |
+------+----------+--------------+--------+----------+----------+
Table: City
+------+-----------+---------+
| id | name | stateid |
+------+-----------+---------+
| 1| KL | 1 |
| 2| Georgetown| 2 |
| 3| JB | 3 |
+------+-----------+---------+
Table: State
+------+---------------------+-----------+
| id | name | countryid |
+------+---------------------+-----------+
| 1| Wilayah Persekutuan | 1 |
| 2| Penang | 2 |
| 3| Johor | 3 |
+------+---------------------+-----------+
Table: Country
+------+-----------+
| id | name |
+------+-----------+
| 1| Malaysia |
| 2| Singapore |
+------+-----------+
上記は、いくつかのサンプルデータを含む私のテーブルです。要件は、ユーザーがキーワードを入力してService
テーブルで検索を開始できるようにすることです。キーワードがサービス名と一致する場合は、Service
からその結果を返します。そうでない場合は、City
テーブルでキーワードを検索します。キーワードが特定のCity
行と一致する場合は、Service
でその都市に一致するすべてのcityid
行を取得します。行が見つからない場合は、State
テーブルまで、Country
テーブルの検索を続けます。
だからここに私の解決策の問題があります。ユーザーが国名を入力すると、Country
テーブルから一致結果を取得し、その結果のcountryid
を使用してState
テーブル内のすべての関連する州を取得し、それらの結果のstateid
sを使用してCity
テーブル–そして、最後の結果のcityid
を使用して、Service
テーブル内の関連サービスを検索します。多くの再帰的なIN
検索を使用して結果をフィルター処理する必要があるため、これは良い方法ではないと思いますが、これはパフォーマンスには適していません。
その後、私の同僚は別の解決策を考え出しました。この解決策は、検索に関連するすべての参照をService
テーブルに入れます。次のようなものです。
Solution 2
Table: Service
+------+----------+--------------+--------+----------+-----------+----------+----------+
| id | name | description | cityid | stateid | countryid | address1 | address2 |
+------+----------+--------------+--------+----------+-----------+----------+----------+
| 1| Service1 | Description1 | 1 | 1 | 1 | Address1 | |
| 2| Service2 | Description2 | 2 | 2 | 2 | Address2 | |
| 3| Service3 | Description3 | 3 | 3 | 3 | Address3 | |
+------+----------+--------------+--------+----------+-----------+----------+----------+
したがって、Country
が検索に一致した場合、結果のcountryid
を使用してService
テーブルで直接検索を実行します。それ以外の場合は、State
が検索に一致した場合、結果のstateid
を使用してService
などで検索を実行します。この方法は再帰的な検索が少ないためより効率的ですが、欠点は正規化の慣行に違反していることです。Service
テーブルには、countryid
とstateid
という冗長な情報があります。そして論理的に言えば、複数のルックアップ/結合を実行する必要がある場合でも、cityid
だけでこれら2つのIDを見つけることができます。
それで、私はどの解決策をとるべきですか?それとももっと良い提案がありますか?お知らせ下さい。
私はあなたが述べたテクニックのすべてを展開したくなるでしょう。説明させてください。正規化されたService/City/State/Countryは、OLTP処理に最適です。アプリケーションのその部分のために保持し、実際のデータストアとして扱います。
あなたが述べたように、これは検索を複雑にする可能性が高いので、非正規化が有利になるでしょう。あなたの提案とは異なり、検索を支援するためだけに存在する別のテーブルに非正規化します。実際のコンピュータ上の実際のDBMSには、理論的なコンピュータ科学者が想像したいパフォーマンス特性がないことは、明確に認められています。別のテーブルに配置することにより、妥協された物理的な必要性を、好ましい正規化された設計から分離します。
最後に、この非正規化されたテーブルの1つの列にすべての検索用語を組み合わせます。 1つの方法は、サービス名、都市名、州名、国名をスペースで区切り、その列にフルテキストインデックスを配置することです。
もう1つは、id
、name
およびsource_table
の3つの列を持つことです。最初の2つは、質問にリストされているデータ値からのものです。 source_table
は、これがどの正規化テーブルからのものかを示しています。質問からのサンプルデータは次のようになります。
id name source_table
----- -------- ------------
1 Service1 Service
2 Service2 Service
...
1 KL City
3 JB City
...
2 Penang State
..
1 Malaysia Country
クエリは、ユーザーの検索用語をこのテーブルのname
列と照合します。
select
id,
source_table
from search_table
where name = <user-provided value>
返されたid
およびsource_table
を使用して、正規化されたテーブルから選択する値を決定できます。これらは通常の方法で相互に結合できます。階層には4つのレイヤーがあるため、可能なクエリは4つだけです(サービス、サービス/都市、サービス/都市/州、サービス/都市/州/国)。次のように適切なものを呼び出すのは簡単です。
if source_table = "Service"
select
<columns>
from Service
where id = <search_table.id>
if source_table = "City"
select
<columns>
from Service
inner join City
on Service.cityid = City.id
where City.id = <search_table.id>
... etc.
名前がこれらのレベル(モナコなど)で重複している可能性があることに注意して、これらのケースをどのように処理するかを決定してください。
名前が一意であることが保証され、代理キーではなく自然キーが使用された場合、プロセスは多少簡略化できます。
しかし、このすべての努力が価値があるかどうかは疑問です。これらのテーブルには何行ありますか?ユーザーの検索値をORを使用して各名前列と比較する、インデックス付きの正規化されたテーブルに対するクエリのパフォーマンスはどのくらい悪いですか?代表的な量のダミーデータを作成し、プロダクションのようなハードウェアで応答時間を測定することにより、開発と多くのメンテナンス作業を節約できます。
特定の都市、州、または国の存在によって地理的近接度を測定する代わりに、地理的ポイント(緯度/経度)または地理的ポリゴンのいずれかを使用してサービスを検索してみませんか?
あなたが提案しているスキームはこれらの状況を考慮に入れていません:
MySQLには空間拡張機能があります。これらを調べて、サーチャーに「近い」(コンテキストでの意味が何であれ)サービスを検索できるようにすることで、問題をより直接的に解決できるかどうかを確認する必要があります。
City + State + Countryは1つのLocationテーブルにある必要があります。または...冗長性がほとんどない場合は、それらをリンクするテーブルに追加します。
つまり、「正規化しすぎる」というものがあります。過度に正規化すると、検索がより複雑になり、クエリが遅くなる可能性があります。一方、正規化の利点このタイプのデータはごくわずかです。
description
には何がありますか?ユーザーが検索する可能性のある単語の束?その場合は、FULLTEXT
が最適です。ただし、制限があることに注意してください。
country
にCHAR(2)
を使用する場合は、CHAR(2) CHARACTER SET ascii
にします。デフォルトの文字セットがutf8mb4の場合、CHAR(2)
は8バイトを使用します。 asciiを指定すると、2バイトに抑えられます。必要なのはこれだけです。
「最も近い」を検索する必要がある場合、それは別の魚のやかんです。あなたはここで多くの議論を見つけるでしょう: https://stackoverflow.com/questions/tagged/latitude-longitude 。