2010-09-16 4 views
2

Предположим, у меня есть три таблицы: user, group и xref, таблица, которая дает им много-много RI.Как я могу проверить определенное значение во всех агрегированных строках?

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

select 
    user.user_id, 
    user.user_name, 
    count(*) as group_count 
from 
    user 
     inner join xref on user.user_id = xref.user_id 
     inner join group on group.group_id = xref.group_id 
group by user.user_id, user.user_name 

Все в порядке до сих пор. Но что, если мне нужна дополнительная информация? Я сообщаю об этом, и я хочу знать, является ли каждый пользователь разработчиком или Менеджером контента. Теперь, анти-закономерность:

select 
    user.user_id, 
    user.user_name, 
    count(*) as group_count, 
    max(case group.group_name when 'Developers' then 'Y' else null end) 
     as is_dev 
    max(case group.group_name when 'Content Management' then 'Y' else null end) 
     as is_cm 
from 
    user 
     inner join xref on user.user_id = xref.user_id 
     inner join group on group.group_id = xref.group_id 
group by user.user_id, user.user_name 

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

«Для каждого пользователя, покажите мне, сколько групп они в Кроме того, для всех имен групп для каждого пользователя, покажите мне, если„Разработчики“это один из. ценности."

То, что я на самом деле запрашиваемое это:.

«Для каждого пользователя, покажи мне, сколько групп они в Кроме того, для всех имен групп для каждого пользователя, покажи мне самое высокое значение, вырабатываемое этим выражением case. "

Причина, по которой это анти-модели является то, что я в основном полагаясь на то, что Yпроисходит к «пузыриться» выше null при оценке с max(). Если кто-то захотел скопировать или увеличить этот запрос, они могли бы легко забыть о анти-шаблоне и случайно изменить возвращаемые значения на то, что не использует одно и то же неинтуитивное совпадение.

В принципе, запрос я хотел бы написать это:

select 
    user.user_id, 
    user.user_name, 
    count(*) as group_count, 
    any(group.group_name, 'Developers', 'Y', null) as is_dev, 
    any(group.group_name, 'Content Management', 'Y', null) as is_cm 
from 
    user 
     inner join xref on user.user_id = xref.user_id 
     inner join group on group.group_id = xref.group_id 
group by user.user_id, user.user_name 

Я просеивание вокруг вариантов, и, кажется, есть несколько потенциалов:

  • first_value мог но я не могу понять, как ограничить соответствующее окно partition правильными строками.
  • Аналитические функции с предложением over могут работать, но I do хотите свернуть столбцы, которые я группирую, поэтому он не выглядит идеально.
  • Беспокойно, кажется, есть функция any, зарегистрированная here, но она существует только в таинственном диалекте, называемом Oracle OLAP DML, который я не думаю, что могу получить доступ, используя только SQL на 10g. Но, кажется, точно что я хочу.

Это все, что у меня есть. Есть идеи?

Я признаю, что есть две очень простые идеи: «Сделайте это в коде» или «Сделайте это в PL/SQL», но это обман.:-)

+0

может принадлежать пользователю как разработчикам, так и контент-менеджеров? это причина, по которой вам нужны два отдельных столбца? –

+0

Вместо того чтобы помещать [SQL] в заголовок, я просто полагаюсь на тег «sql». –

+0

@ вот сейчас. Да, вот почему. –

ответ

3

Я бы переключился с MAX на SUM (с 1, а не на Y), поэтому вы говорите: «Подсчитайте количество групп, в которых находится этот человек, где имя группы -« Разработчики ».

Затем шаблон похож на «подсчет количества продаж, где стоимость покупки составляет более 30 долларов».

Вы можете, при желании, добавить еще одно выражение: «Если число больше нуля, то« да »этот человек является разработчиком». Очень явный и, вероятно, не нужен.

+0

Упрощенный, потому что 'SUM()' кажется менее обходным путем. Но сейчас я собираюсь принять любые ответы. Благодаря! –

2
SELECT user.user_id, 
     user.user_name, 
     COUNT(*) group_count, 
     COUNT(DISTINCT DECODE(group_name, 'Developers', 'Y', NULL)) AS is_developer 
     COUNT(DISTINCT DECODE(group_name, 'Content Management', 'Y', NULL)) AS is_content_manager 
FROM the_query 

Что касается ANY, это предикат, похожий на IN, а не функция:

SELECT * 
FROM dual 
WHERE 'baz' = ANY('foo', 'bar', 'baz') 
0

Я предпочитаю Gary's answer, но если вы хотите придерживаться булевой ответ вы могли бы сделать заказ более явно, возвращая «N» вместо null.

select 
    user.user_id, 
    user.user_name, 
    count(*) as group_count, 
    max(case group.group_name when 'Developers' then 'Y' else 'N' end) 
     as is_dev 
    max(case group.group_name when 'Content Management' then 'Y' else 'N' end) 
     as is_cm 
from 
    user 
     inner join xref on user.user_id = xref.user_id 
     inner join group on group.group_id = xref.group_id 
group by user.user_id, user.user_name 

(+1 за хорошо написан вопрос)

+0

Спасибо за +1 и ответьте. К сожалению, «N» становится жертвой той же проблемы, о которой я упомянул в моем вопросе, а именно о том, что вы полагаетесь на то, что «N» просто сортируется выше «Y». Например, если вместо «Y» и «N» вы использовали «Always» и «Never», то каждая ячейка была бы «Never», потому что «max» всегда будет ее подбирать. Это неинтуитивный «gotcha», который, я считаю, является частью того, что делает его настоящим анти-шаблоном. –

+0

Как я уже сказал, я предпочитаю подсчет, но я думаю, что вы очень безопасны, используя Y и N как флаг. –

Смежные вопросы