0

私は最近、複雑なSQLクエリに苦労しています。
私は次のテーブルを持っています:

[dbo].[User] ~ {ID,nickname}  
[dbo].[Property] ~ {ID,title}  
[dbo].[Property_Values] ~ [ID,propertyID,title}  
[dbo].[Property_Values_User_Linkage] ~ {UserID,PropertyID,valueID}  

これは基本的に、ユーザーが各プロパティの値を選択するプロジェクトです。各プロパティは、単一値または複数値にすることができます。たとえば、ユーザーはプロパティ{ID = 1、title = Hobbies}に複数の値を選択できますが、プロパティ{ID = 2、title=HairColor}には単一の値を選択する必要があります。

別のテーブルを使用する-[dbo]。[Search_Property_Values_User_Linkage]-{UserID、PropertyID、valueID}必要なプロパティを選択しており、一致するユーザーを見つけることを期待しています。ただし、HairColorなどの値(または複数値)を選択していない場合は、すべてのユーザーを取得する必要があります(HairColorでフィルター処理したくないため)。

これまでのところ簡単ですが、私が解決できないと思われる問題は、複数の値が先にあるか、ユーザー定義の値がない場合です。たとえば、HairColor =BrownとHobbiesIN(basketball、football)のすべてのユーザーが必要です。

いずれかの用語に一致するすべてのユーザー(および、フィルターを選択していないため、他のプロパティを持つユーザー)を取得できますが、条件に完全に一致するユーザーのみを取得することはできません。

コードを言葉にするために、次のようなすべてのユーザーが必要だとしましょう。

  • 私が選択したすべてのプロパティ値に一致する
  • EyesColorなどの他のプロパティがある場合もありますが、フィルタリング値を選択していないため、それらも取得される可能性があります。
  • プロパティがまったく設定されていない可能性がありますが、このプロパティの値を選択していないため、有効です。
  • 私が選択した1つのプロパティだけでなく、選択したすべてのプロパティをグループとして一致させます(バスケットボールが好きで、HairColorに「赤」が付いているユーザーは無効です!

選択されていない値をビット単位で「補完」する仮想テーブルを作成するソリューションに出くわしました。たとえば(実際のコードではありません):

DECLARE @isMatches bit  
SET @isMatches=0    
if [propertyIsChosen]=1  
{    
 if [userInProperty]=1 SET @isMatches=1   
}  
else SET isMatches=1

基本的に、[Property] WITH [User]をCROSS-
JOINし、残りのテーブルをLEFT-OUTER-JOINして、選択に一致させます。
すべてのユーザーとそのプロパティへの一致を取得します。茶色の髪のユーザー、バスケットボール/サッカーが好きなユーザーはいますが、両方に一致するユーザー(そしてもちろん私が他の未定義のプロパティ)を持っているユーザーはいないので、これでは十分ではありません。
これは重いですが、問題を分析するためにこれまでに得たものです。

助けていただければ幸いです。10年前の数学の授業で何かが足りないと思います...

編集:db pic:http: //i51.tinypic.com/2n1cfwg.png

4

1 に答える 1

0

データ・モデル

あなたのコメントから、あなたはあなたの質問で言及していない他のいくつかのプロパティ関連のテーブルを持っていることがわかります。今のところこれらを無視して、プロパティ値の表現に集中できると仮定します。これは、以下の簡略化されたモデルで実行できます。

ここに画像の説明を入力してください

DDL SQL(MS SQL Server):

CREATE TABLE [USER] (
    USER_ID int NOT NULL,
    NICKNAME nvarchar(50) NOT NULL,
    CONSTRAINT PK_USER PRIMARY KEY CLUSTERED (USER_ID)
);

CREATE TABLE USER_PROPERTY_VALUE(
    USER_ID int NOT NULL,
    PROPERTY_NAME nvarchar(50) NOT NULL,
    PROPERTY_VALUE_NO int NOT NULL,
    PROPERTY_VALUE nvarchar(255) NOT NULL,
    CONSTRAINT PK_USER_PROPERTY_VALUE PRIMARY KEY CLUSTERED (
        USER_ID,
        PROPERTY_NAME,
        PROPERTY_VALUE_NO
    )
);

ALTER TABLE USER_PROPERTY_VALUE ADD CONSTRAINT FK_USER_PROPERTY_VALUE_USER FOREIGN KEY(USER_ID)
REFERENCES [USER] (USER_ID);

複数値は、USER_PROPERTY_VALUEの複数の行で表され、同じPROPERTY_NAMEを共有しますが、それぞれが異なるPROPERTY_VALUE_NOを持ちます。

次のサンプルデータ...

Rob:   HairColor={brown}, Hobby={basketball,football}
Bob:   HairColor={brown}, Hobby={basketball}
Steve: Hobby={basketball,football}

...データベースでは次のように表されます。

ユーザー:

USER_ID NICKNAME
1       Rob
2       Bob
3       Steve

USER_PROPERTY_VALUE:

USER_ID PROPERTY_NAME   PROPERTY_VALUE_NO   PROPERTY_VALUE
1       HairColor       1                   brown
1       Hobby           1                   basketball
1       Hobby           2                   football
2       HairColor       1                   brown
2       Hobby           1                   basketball
3       Hobby           1                   basketball
3       Hobby           2                   football

クエリの例

茶色の髪の色のユーザーを選択します。

SELECT *
FROM [USER]
WHERE
    EXISTS (
        SELECT *
        FROM USER_PROPERTY_VALUE 
        WHERE
            USER_PROPERTY_VALUE.USER_ID = [USER].USER_ID
            AND PROPERTY_NAME = 'HairColor'
            AND PROPERTY_VALUE = 'brown'
    )

結果:

USER_ID NICKNAME
1       Rob
2       Bob

趣味にバスケットボールとサッカーの両方が含まれるユーザーを選択します。

SELECT *
FROM [USER]
WHERE
    EXISTS (
        SELECT *
        FROM USER_PROPERTY_VALUE 
        WHERE
            USER_PROPERTY_VALUE.USER_ID = [USER].USER_ID
            AND PROPERTY_NAME = 'Hobby'
            AND PROPERTY_VALUE = 'basketball'
    )
    AND EXISTS (
        SELECT *
        FROM USER_PROPERTY_VALUE 
        WHERE
            USER_PROPERTY_VALUE.USER_ID = [USER].USER_ID
            AND PROPERTY_NAME = 'Hobby'
            AND PROPERTY_VALUE = 'football'
    )

結果:

USER_ID NICKNAME
1       Rob
3       Steve

髪の色が茶色で、趣味にバスケットボールとサッカーの両方が含まれるユーザーを選択します。

SELECT *
FROM [USER]
WHERE
    EXISTS (
        SELECT *
        FROM USER_PROPERTY_VALUE 
        WHERE
            USER_PROPERTY_VALUE.USER_ID = [USER].USER_ID
            AND PROPERTY_NAME = 'HairColor'
            AND PROPERTY_VALUE = 'brown'
    )
    AND EXISTS (
        SELECT *
        FROM USER_PROPERTY_VALUE 
        WHERE
            USER_PROPERTY_VALUE.USER_ID = [USER].USER_ID
            AND PROPERTY_NAME = 'Hobby'
            AND PROPERTY_VALUE = 'basketball'
    )
    AND EXISTS (
        SELECT *
        FROM USER_PROPERTY_VALUE 
        WHERE
            USER_PROPERTY_VALUE.USER_ID = [USER].USER_ID
            AND PROPERTY_NAME = 'Hobby'
            AND PROPERTY_VALUE = 'football'
    )

結果:

USER_ID NICKNAME
1       Rob

等...

別のテーブルの内容に基づくクエリ

フィルタ条件を含むテーブルがあるとします。

CREATE TABLE PROPERTY_FILTER (
    PROPERTY_NAME nvarchar(50) NOT NULL,
    PROPERTY_VALUE nvarchar(255) NOT NULL,
    CONSTRAINT PK_PROPERTY_FILTER PRIMARY KEY (PROPERTY_NAME, PROPERTY_VALUE)
)

次のクエリは、そのテーブルに現在含まれている基準を満たすユーザーのみを返します。

SELECT *
FROM [USER] U
WHERE
    NOT EXISTS (
        SELECT F.PROPERTY_NAME, F.PROPERTY_VALUE
        FROM PROPERTY_FILTER F
        EXCEPT
        SELECT P.PROPERTY_NAME, P.PROPERTY_VALUE
        FROM USER_PROPERTY_VALUE P
        WHERE P.USER_ID = U.USER_ID
    )

平易な英語:ユーザーのプロパティでもないフィルタープロパティがある場合は、そのユーザーを無視します。

ところで、これは同時マルチユーザー環境では機能しません。「セッション」を識別するためにPROPERTY_FILTERテーブルに追加のフィールドを導入するか、一時テーブルを使用する必要があります(永続性が必要ない場合)。

于 2011-10-23T20:15:54.193 に答える