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 和 Hobbies IN(basketball,football) 的用户。

我可以检索与其中一个术语匹配的所有用户(以及具有其他属性的用户,因为我没有选择过滤它们),但我不能只获取完全符合我的条件的用户。

要将代码转化为文字,假设我需要所有用户:

  • 匹配我选择的所有属性值
  • 可能有其他属性,例如 EyesColor,但由于我没有选择过滤值,它们也可能会被检索到。
  • 可能根本没有设置任何属性,但由于我没有为此属性选择值,所以它们是有效的!
  • 将所有选定的属性作为组匹配,而不仅仅是我选择的一个属性(喜欢篮球但头发颜色为“红色”的用户是无效的!

我遇到了一个解决方案,在该解决方案中,我创建了一个虚拟表,该表按位“完成”未选择的值。例如(不是实际代码):

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

我基本上 CROSS-JOIN [Property] WITH [User]
和 LEFT-OUTER-JOIN 其余的表来匹配选择。
我让所有用户和他们匹配到一个属性。这还不够好,因为我得到了棕色头发的用户,喜欢篮球/足球的用户,但没有同时匹配两者的用户(当然还有我未定义的任何其他属性)。
这很重,但这是我到目前为止分析问题的内容。

我将不胜感激任何帮助。我想我在 10 年前的数学课上遗漏了一些东西……

编辑:数据库图片:http: //i51.tinypic.com/2n1cfwg.png

4

1 回答 1

0

数据模型

从您的评论中,我看到您还有其他一些您在问题中没有提到的与属性相关的表格。我假设我们现在可以忽略这些,只专注于表示属性值,这可以通过下面的简化模型来完成:

在此处输入图像描述

DDL SQL(MS SQL 服务器):

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 回答