Расширенный (?) И / ИЛИ запрос - PullRequest
2 голосов
/ 10 июля 2009

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

Сам запрос на данный момент выглядит так:

SELECT 
  p.PersonID   
FROM 
  Person p,     
  ( SELECT DISTINCT PersonID, CriteriaID 
    FROM PersonCriteria 
    WHERE CriteriaID in (#list_of_ids#)     
  ) k     
WHERE 
  p.PersonID= k.PersonID     
GROUP BY 
  p.PersonID     
HAVING 
  Count(*) = #Listlength of list_of_ids#

Пока проблем нет и все работает отлично.

Теперь я хочу предложить пользователю возможность добавлять некоторые переменные И и ИЛИ в свой поиск, т.е. кто-то может сказать:

Я ищу человека, который обладает: Критерии 1 И 3 И 4 (что будет охватываться запросом выше) И (5 ИЛИ 6 ИЛИ 7) И (8 ИЛИ 9) и так далее ...

Я не уверен, с чего начать с этого дополнительного уровня. Я надеюсь, что кто-то еще делает .. :-)

Ответы [ 4 ]

2 голосов
/ 10 июля 2009

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

  • Определяемые пользователем агрегатные функции. Может быть, вы можете сделать функцию, которая принимает в качестве аргумента желаемое выражение (в упрощенном синтаксисе) и строки для одного человека. Затем функция анализирует выражение и сопоставляет его со строками. Хм ... может быть, MySQL включает в себя некоторую функцию агрегирования и функцию сопоставления регулярных выражений? Тогда это может быть решением (хотя, вероятно, не очень быстрым).
  • Аналитические функции. Я не делаю вид, что понимаю их, но, насколько я знаю о них, я думаю, что они в основном в этом направлении. Хотя я не знаю, найдется ли функция, которая удовлетворит эту потребность.

Добавлено: Ах, я думаю, я понял! Хотя я думаю, что спектакль будет ужасным. Но это будет работать! Например, если у вас есть требование для поиска 1 AND 2 AND (3 OR 4), вы должны написать:

SELECT
    *
FROM
    Persons A
WHERE
    EXISTS (Select * from PersonCriteria B WHERE A.PersonID=B.PersonID AND CriteriaID=1)
    AND
    EXISTS (Select * from PersonCriteria B WHERE A.PersonID=B.PersonID AND CriteriaID=2)
    AND
    (
        EXISTS (Select * from PersonCriteria B WHERE A.PersonID=B.PersonID AND CriteriaID=3)
        OR
        EXISTS (Select * from PersonCriteria B WHERE A.PersonID=B.PersonID AND CriteriaID=4)
    )

Добавлено 2: Вот еще один, хотя производительность, вероятно, будет еще хуже:

SELECT p.* FROM Person p
    JOIN (select PersonID from PersonCriteria WHERE CriteriaID=1) c1 ON p.PersonID=c1.PersonID
    JOIN (select PersonID from PersonCriteria WHERE CriteriaID=2) c2 ON p.PersonID=c2.PersonID
    JOIN (select PersonID from PersonCriteria WHERE CriteriaID IN (3,4)) c3 ON p.PersonID=c3.PersonID

Добавлено 3: Это вариант № 2, но на самом деле он может иметь достойную производительность!

SELECT p.* FROM
    Person p
    JOIN PersonCriteria c1 on (p.PersonID=c1.PersonID AND c1.CriteriaID=1)
    JOIN PersonCriteria c2 on (p.PersonID=c2.PersonID AND c2.CriteriaID=2)
    JOIN PersonCriteria c3 on (p.PersonID=c3.PersonID AND c3.CriteriaID IN (3,4))

Если вы добавите индекс к PersonCriteria для столбцов (PersonID, CriteriaID) (именно в таком порядке!), То я думаю, что он будет таким же быстрым, как и в любом случае.

1 голос
/ 10 июля 2009

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

SELECT   p.PersonID   
FROM   Person p
JOIN       
(SELECT DISTINCT PersonID    
FROM PersonCriteria     
WHERE CriteriaID in (1,2,3) and count(criteriaID) = 3) k  
       on p.PersonID =    k.PersonID  
JOIN
   (SELECT DISTINCT PersonID    
FROM PersonCriteria     
WHERE CriteriaID in (4,5) ) k2  on p.PersonID = k2.PersonID
JOIN
   (SELECT DISTINCT PersonID    
FROM PersonCriteria     
WHERE CriteriaID in (5,6,7) ) k3  on p.PersonID = k3.PersonID
JOIN
   (SELECT DISTINCT PersonID    
FROM PersonCriteria     
WHERE CriteriaID in (8,9) ) k4  on p.PersonID = k4.PersonID 

То, как я это интерпретирую. первое соединение - это производная таблица, в которую попадает любой, у кого есть все три из указанных условий. Когда последующие производные таблицы находят людей, которые удовлетворяют одному из этих условий (в основном часть OR), путем объединения с остальными производными таблицами, мы выполняем часть AND запроса. Я также знаю, что синтаксис проходит проверку синтаксиса для SQL Server, может потребоваться настройка для MYSQL.

1 голос
/ 10 июля 2009

Вы можете значительно упростить это, например, выполнив:

ВЫБЕРИТЕ DISTINCT PersonID FROM PersonCriteria ГДЕ КритерийID В (1,2) ИЛИ CriteriaID IN (8,9)

Также рассмотрите возможность использования JOIN вместо суб-выбора (для исполнения)

0 голосов
/ 10 июля 2009

Если вам нужно предложить более «динамический» подход к поиску ваших данных, SQL станет действительно уродливым и длинным и не будет по-настоящему динамичным, и ... я упомянул «уродливый»?

Я использую ORM-фреймворки для этой задачи, и они отлично справляются с работой.

Но если ваша структура соответствует описанному вами (с ИЛИ, заключающим в себе множество условий И), то при условии, что ваша текущая реализация И-только находится в UDF с именем dbo.getPersonForAndCriteria(...), вы можете реализовать ИЛИ, просто используя UNION:

dbo.getPersonForAndCriteria(@myListOfIDs1) --// works for AND
UNION -- replaces OR
dbo.getPersonForAndCriteria(@myListOfIDs2) --// works for AND
UNION -- replaces OR
dbo.getPersonForAndCriteria(@myListOfIDs3) --// works for AND

Примечание: это только для иллюстрации, но я бы обернул вашу процедуру в красивый UDF, который принимает список параметров (идентификаторов) в виде таблицы (используя XML или строку с разделителями-запятыми, которая затем анализируется внутри UDF), затем просто сделайте хорошее JOIN в этом списке / таблице вместо WHERE ... IN части, и последняя часть станет COUNT (*) = COUNT (SELECT ID FROM myFilterTable).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...