我最近一直在努力解决复杂的SQL查询问题 我有以下表格:
[dbo].[User] ~ {ID,nickname}
[dbo].[Property] ~ {ID,title}
[dbo].[Property_Values] ~ [ID,propertyID,title}
[dbo].[Property_Values_User_Linkage] ~ {UserID,PropertyID,valueID}
它基本上是一个用户为每个属性选择值的项目。每个属性可以是单值或多值。例如,用户可能会为Property {ID = 1,title = Hobbies}选择多个值,但必须为Property {ID = 2,title = HairColor}选择一个值。
使用另一个表 - [dbo]。[Search_Property_Values_User_Linkage] - {UserID,PropertyID,valueID}我正在选择我想要的属性,我希望找到匹配的用户。但是,如果我没有选择一个值(或多值),让我们说HairColor,我应该得到所有用户(因为我不想通过HairColor过滤)。
到目前为止,这很容易,但我似乎无法解决的问题是前方存在多值或没有用户定义值的情况。例如,我希望所有用户都使用HairColor = Brown和Hobbies IN(篮球,足球)。
我可以检索所有符合其中一个条款的用户(以及那些拥有其他属性的用户,因为我没有选择过滤它们),但我不能只获得绝对的用户符合我的标准。
要将代码放入文字中,我们只需说我需要以下所有用户:
我遇到了一个解决方案,在其中我创建了一个虚拟表,它按比特“完成”未选择的值。例如(不是实际代码):
DECLARE @isMatches bit
SET @isMatches=0
if [propertyIsChosen]=1
{
if [userInProperty]=1 SET @isMatches=1
}
else SET isMatches=1
我基本上CROSS-JOIN [属性]与[用户]
和LEFT-OUTER-JOIN其余表格以匹配选择
我得到了所有用户和他们与房产的匹配。这不够好,因为我有棕色头发的用户,喜欢篮球/足球的用户,而不是那些与两者相匹配的用户(当然还有任何其他未定义的属性)。
这很重,但这是我到目前为止分析问题所得到的。
我将不胜感激任何帮助。 10年前,我想我的数学课上缺少一些东西......
编辑:db pic:http://i51.tinypic.com/2n1cfwg.png
答案 0 :(得分: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:
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
Etc等......
假设您有一个包含过滤条件的表:
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表中引入一个额外字段来标识“会话”,或者使用temporary tables(如果您不需要)持久性)。