2010-12-02 12 views
0

私は人のテーブルを持っています。 :複数の条件に基づいて検索レコードを保存する

CREATE TABLE [Contact](
[ContactID] [bigint] IDENTITY(1,1) NOT NULL, 
[ContactType] [nvarchar](50) NULL, 
[Forename] [nvarchar](60) NULL, 
[Surname] [nvarchar](60) NULL, 
[Company] [nvarchar](60) NULL 
} 

Example Data : 
01, "Student", "Bob", "Smith", Blank 
02, "Staff", "Robert", "Smithe", "Roberts And Sons" 
Etc 

この表には、すべての連絡先に共通するすべてのフィールドが含まれています。しかし、私はいくつかの "タイプ"の連絡先を持っている場合とそうでない場合があります。たとえば、レコードに "ContactType = 'student'"がある場合、私は "studentid"という余分なフィールドを保存します。わずかに異なるフィールド要件を持つ多くの異なるタイプの連絡先がそれぞれあります。将来、このような状況に追加するには、各連絡先タイプに追加のフィールドが追加される可能性があります。

連絡先表に各フィールドを追加すると、99%のレコードには必要ない多くのフィールドが表示されます。私は、フィールドの名前と値で、このテーブルにレコードを追加し、余分な各フィールドについて

CREATE TABLE [ContactMetaData](
[ContactID] [bigint] NOT NULL, 
[PropName] [nvarchar](200) NOT NULL, 
[PropData] [nvarchar](200) NULL 
) 

Example Data: 
01, "StudentID", "" 
01, "CourseName", "IT" 
01, "Average", "10" 
02, "Ranking", "22" 
02, "ProductTypes", "IT Equipment" 
ETC 

:だから私はこのような第二のテーブルを作成する上で計画していました。私は、私の質問は

ある

などの情報をプルアップするためのコードを使用することができ、私はすべての単一のフィールドを持つ巨大なテーブル以外の別の方法の困惑として最善のアプローチこれです。このアプローチを考えると、プロパティフィールドの多くに複雑なクエリーを実行することが可能です。例えばどのように私は "D"で始まる "forename"の10の "平均" 10の "IT"コースのすべての学生をリストするでしょうか?

答えて

0

はい、実際に使用されていない多くのフィールドがある場合は、この方法(EAV data structureと呼ばれます)に進みます。通常のモデルで行うことができるこの構造のすべてのクエリを実行できます。適切な結合を使用してピボットするだけです。

+0

MySQLを使用していないので、これらの結合のパフォーマンスも良い可能性があります。 – AndreKR

+0

EAV構造を調べるために応答が途切れてしまったことに感謝します。 – Jammy

0

提案されたプロパティベースのアプローチは妥当です。特に、スキーマを変更せずに実行時に新しい連絡先の種類とプロパティを作成する必要がある場合は妥当です。

連絡先の種類とプロパティのセットが実行時に固定されている場合は、各タイプの補助テーブルの作成を検討することもできます。つまり、StudentInfo,、StaffInfoなどのテーブルを作成します。ContactTypeフィールドは、対応する補助テーブルの行が存在する場合に暗黙的に使用する必要がなくなります。このモデルは、誰かが2つのカテゴリに分類される厄介な状況も処理します。スタッフでもある学生。

しかし、これをすべて無視して、プロパティテーブルのクエリに関する2番目の質問を見てみましょう。このようなクエリを実行できます。あなたの例では、このクエリによって答えられるでしょう:

SELECT * 
FROM Contact AS c 
INNER JOIN ContactMetaData AS crs 
    ON crs.ContactId = c.ContactId 
    AND crs.PropName = 'CourseName' 
    AND crs.PropData = 'IT' 
INNER JOIN ContactMetaData AS av 
    ON av.ContactId = c.ContactId 
    AND av.PropName = 'Average' 
    AND av.PropData = 10 
WHERE c.Forename LIKE 'D%' 

トリックは一度テストしたい各カスタムプロパティのために、ContactMetaDataテーブルに複数回参加することです。

このスタイルの問合せの問題は、実行時にほぼ確実に生成する必要があることです。結局のところ、プロパティーのセットは実行時に動的です。これは、動的プロパティの基準は、一時テーブルに挿入されたものによって決定されるクエリは、今、実行時に固定されるという利点を有している

DECLARE @propertyCriteria TABLE (
    PropName NVARCHAR(200) NOT NULL, 
    PropData NVARCHAR(200) NULL 
) 

INSERT INTO @propertyCriteria VALUES ('CourseName', 'IT') 
INSERT INTO @propertyCriteria VALUES ('Average', '10') 

SELECT * 
FROM Contact AS c 
WHERE c.Forename LIKE 'D%' 
AND NOT EXISTS (
    SELECT * 
    FROM @propertyCriteria AS crit 
    LEFT JOIN ContactMetaData AS meta 
    ON meta.ContactId = c.ContactId 
    AND meta.PropName = crit.PropName 
    AND meta.PropData = crit.PropData 
    WHERE meta.ContactId IS NULL 
) 

:あなたは、クエリを表現する別のアプローチを取ることによってこれを避けることができます@propertyCriteria。欠点は、現在一時テーブルが必要であり、内部結合で実行した場合と同様にクエリが実行されないことです(ただし、データベースに50,000レコードしかない場合は違いは見られません)。また、この第2のアプローチは、一緒にANDされた基準に対してのみ機能することにも注意してください。 ORを必要とする場合、ソリューションはさらに複雑になります。

ちなみに、プロパティ値と条件の両方にNULLを許可する場合は、NULLを含む比較演算子が常にfalseを返すことを忘れないでください(NULL = NULLはfalse、NULLは<> NULLはfalseなど)。

関連する問題