Фильтрация SQL по нескольким элементам в одном столбце - PullRequest
4 голосов
/ 25 августа 2009

У меня есть две таблицы в SQL, одна с проектом и одна с категориями, к которым принадлежат проекты, т. Е. JOIN будет выглядеть примерно так:

Project | Category
--------+---------
  Foo   | Apple
  Foo   | Banana
  Foo   | Carrot
  Bar   | Apple
  Bar   | Carrot
  Qux   | Apple
  Qux   | Banana

(Строки заменены на идентификаторы из более высокой нормальной формы, очевидно, но здесь вы поймете смысл).

Что я хочу сделать, так это разрешить фильтрацию так, чтобы пользователи могли выбирать любое количество категорий, и результаты будут фильтроваться по элементам, которые являются членами всех выбранных категорий. Например, если пользователь выбирает категории «Apple» и «Banana», появляются проекты «Foo» и «Qux». Если пользователь выбирает категории «Apple», «Banana» и «Carrot», то отображается только проект «Foo».

Первое, что я попробовал, был простой проект SELECT DISTINCT FROM ... WHERE Category = 'Apple' и Category = 'Banana', но, конечно, это не работает, поскольку Apple и Banana отображаются в одном столбце в две разные строки для любого общего проекта.

GROUP BY и HAVING не приносят мне никакой пользы, поэтому скажите мне: есть ли очевидный способ сделать это, что я упускаю, или это действительно настолько сложно, что мне придется прибегнуть к рекурсивному присоединяется

Кстати, это в PostgreSQL, но, конечно, стандартный SQL-код всегда предпочтительнее, когда это возможно.

Ответы [ 3 ]

7 голосов
/ 25 августа 2009

Подробнее об эффективности смотрите в этой статье в моем блоге:


Решение ниже:

  • Работает на любое количество категорий

  • Более эффективен, чем COUNT и GROUP BY, поскольку он проверяет наличие любой пары проект / категория ровно один раз, без учета.

& застенчивая;

SELECT  *
FROM    (
        SELECT  DISTINCT Project
        FROM    mytable
        ) mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    (
                SELECT  'Apple' AS Category
                UNION ALL
                SELECT   'Banana'
                UNION ALL
                SELECT   'Carrot'
                ) list
        WHERE   NOT EXISTS
                (
                SELECT  NULL
                FROM    mytable mii
                WHERE   mii.Project = mo.Project
                        AND mii.Category = list.Category
                )
        )
4 голосов
/ 25 августа 2009

Поскольку проект может быть в категории только один раз, мы можем использовать COUNT, чтобы снять этот трюк:

SELECT project, COUNT(category) AS cat_count
  FROM /* your join */
  WHERE category IN ('apple', 'banana')
  GROUP BY project
  HAVING cat_count = 2

Проект с категорией только яблоко или банан получит счет 1 и, таким образом, провалит предложение HAVING. Только проект с обеими категориями получит счет 2.

Если по какой-то причине у вас есть дублирующиеся категории, вы можете использовать что-то вроде COUNT(DISTINCT category). COUNT(*) также должно работать и различаться только в том случае, если категория может быть нулевой.

0 голосов
/ 25 августа 2009

Еще одним решением, конечно, является что-то вроде «ВЫБЕРИТЕ РАЗЛИЧНЫЙ ПРОЕКТ ИЗ ... КАК ГДЕ ЯБЛОКО» В (ВЫБЕРИТЕ категорию ОТ ... КАК Б ГДЕ a.Project = b.Project) И «Банан» IN (ВЫБЕРИТЕ категорию ОТ ... КАК ГДЕ a.Project = b.Project) ", но это становится довольно вычислительно дорогим довольно быстро. Я надеялся на что-то более элегантное, и вы, ребята, не разочаровались. Я включил этот в основном для полноты на тот случай, если кто-то еще ответит на этот вопрос. Это явно стоит ноль очков. :)

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