SQL: сопоставление записей по теории множеств - PullRequest
0 голосов
/ 23 октября 2011

В последнее время я боролся со сложным запросом 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 (баскетбол, футбол).

Я могу получить всех пользователей, которые соответствуют одному из терминов (и тех, у кого есть другие свойства, поскольку я не выбрал их фильтрацию), но я не могу получить только пользователей, которые абсолютно соответствует моим критериям.

Чтобы поместить код в слова, скажем, мне нужны все пользователи, которые:

  • Совпадение ALL свойство-значение, которое я выбрал
  • Может иметь другие свойства, такие как EyesColor, но, поскольку я не выбрал значение фильтрации, они также могут быть получены.
  • Возможно, вообще не задано никакого свойства, но, поскольку я не выбрал значение для этого свойства, они ДЕЙСТВИТЕЛЬНЫ!
  • Сопоставьте все выбранные свойства как группу, а не только одно свойство, которое я выбрал (пользователи, которые любят баскетбол, но имеют "красный" цвет для своего HairColor, НЕВЕРНЫ!

Я сталкивалсяс помощью решения, в котором я создаю виртуальную таблицу, которая «дополняет» невыбранные значения битами. Например (не фактический код):

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 лет назад ...

РЕДАКТИРОВАТЬ: db pic: http://i51.tinypic.com/2n1cfwg.png

1 Ответ

0 голосов
/ 24 октября 2011

Модель данных

Из вашего комментария я вижу, что у вас есть другие таблицы свойств, которые вы не упомянули в своем вопросе.Я предполагаю, что мы можем пока игнорировать их и сконцентрироваться на представлении значений свойств, что можно сделать с помощью упрощенной модели, приведенной ниже:

enter image description here

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

И т. Д. И т. Д..

Запросы на основе содержимого другой таблицы

Допустим,у вас есть таблица, содержащая критерии фильтра:

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, чтобы определить«сеанс» или используйте временные таблицы (если вам не требуется постоянство).

...