Oracle - произвольный выбор одной из нескольких строк на основе одного уникального ключа

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

Проблема

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

Cmpd_Key  Group  Subgroup   Group_Desc
  A1    |   A   |    1   |    Same
  A2    |   A   |    2   |    Same
  B1    |   B   |    1   |    Same1
  B2    |   B   |    2   |    Same1
  C1    |   C   |    1   |    Diff1
  C2    |   C   |    2   |    Diff2  <---This field contains multiple values

Мне часто нужно получить уникальный список Group идентификаторов, но требования обычно также требуют поля Group_Desc. К сожалению, из-за слабых ограничений на ввод данных вверх по течению, это поле описания может содержать несколько записей для Group, что приводит к дублированию, поскольку поле Group должно быть уникальным в большинстве извлечений данных. Для моих целей мне все равно, какую Group_Desc запись я извлекаю, пока я могу поддерживать отношение 1 Group к 1 Group_Desc.

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

SELECT Group, Group_Desc
FROM Table t
WHERE Subgroup = (SELECT MIN(Subgroup)
                  FROM Table
                  WHERE Group = t.Group) --Nasty Correlated Subquery

Вопрос

Есть ли у кого-нибудь эффективный трюк для многократного извлечения одной строки из нескольких значений в одном запросе? Я хотел бы иметь возможность отодвигать Group и только первое появляющееся Group_Desc.

Я представляю что-то вроде этого:

SELECT Group, Group_Desc
FROM Table t
GROUP BY Group, Group_Desc    
HAVING ROWNUM = [The lowest returned Rownum within the same Group]

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

Любая помощь, которую вы можете предоставить, будет принята с благодарностью!

----------------EDIT----------------------

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

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

Я не сомневаюсь, что при честном сравнении решение Analytics будет работать наравне или даже лучше, чем решение Correlated SubQuery. Я ценю помощь каждого по этой проблеме!


person DanK    schedule 06.05.2015    source источник
comment
Разве порядок по пункту не сделает то, что вам нужно? например stackoverflow.com/questions/11978136/   -  person bob dylan    schedule 06.05.2015
comment
Мне нравится эта идея, но, к сожалению, нет, я не могу, так как номер подгруппы не соответствует... Я перечислил его здесь для простоты, но на самом деле это может быть множество последовательных номеров.   -  person DanK    schedule 06.05.2015


Ответы (1)


Вы можете использовать min в аналитической версии здесь, это быстро:

select 
    TGroup, 
    min(Group_Desc) over (partition by tgroup) 
  from t

демонстрация SQLFiddle

first_value тоже вариант:

select TGroup,
    first_value(Group_Desc) over (partition by tgroup order by subgroup) gd
  from t
person Ponder Stibbons    schedule 06.05.2015
comment
Это очень интересно. Спасибо! Я раньше не использовал аналитические функции, и мне нужно узнать о них больше. Я думаю, что это может быть ответ, который я ищу, но сразу же я не получаю тот же набор результатов с вашими запросами. Позвольте мне немного поиграть с этими функциями и вернуться к вам. - person DanK; 06.05.2015
comment
Да, запрос 2 работает так же, как и ваш, запрос 1 занимает min(group_desc). Таким образом, для подгрупп (1 'XYZ', 2 'ABC') первая всегда будет принимать 'ABC', а вторая - всегда 'XYZ'. Вы можете использовать эту функцию, которая подходит вам больше. - person Ponder Stibbons; 06.05.2015
comment
Итак, я обнаружил, что это дает мне соответствующие значения полей, которые я хочу, но не устраняет дубликаты. Поэтому, когда у меня есть два описания и я использую функцию OVER, я получаю правильное значение, но получаю его дважды... Сейчас я пытаюсь выяснить, как сгруппировать результаты с помощью аналитической функции. - person DanK; 06.05.2015
comment
Чтобы устранить дубликаты, просто используйте distinct. Но, как я понял, вы хотите отображать каждую строку отдельно, но брать описание из другой строки, как в SQLFiddle (не нужно добавлять никаких уровней с подзапросами). - person Ponder Stibbons; 06.05.2015
comment
+1 Ах.. так просто, что я не могу поверить, что пропустил это. Я просто хочу полностью исключить лишние строки, чтобы Distinct работал хорошо. Теперь я могу повторить свой первоначальный набор, но, к сожалению, это фактически увеличило время выполнения запроса, который я использую для проверки производительности! Тем не менее, мне нравится это решение, и я попытаюсь посмотреть, смогу ли я использовать некоторые индексированные поля, чтобы заставить его работать лучше. - person DanK; 06.05.2015
comment
Есть ли возможность обновить свой вопрос с помощью (упрощенного) запроса, который вы выполняете? По моему опыту, использование аналитических функций значительно ускоряет работу. Я подозреваю, что вы используете этот distinct в подзапросе, и с помощью функции мы можем полностью устранить подзапросы. - person Ponder Stibbons; 06.05.2015
comment
Извините за поздний ответ, но я был занят в конце прошлой недели. Я добавил изменение к проблеме в качестве сводки к своим выводам, но, короче говоря, я думаю, что низкая производительность связана с индексом, который я использую в своем коррелированном подзапросе, который я не могу использовать в вашем аналитическом решении. Хотя в 9/10 случаях я соглашусь, что ваше решение является лучшим, поэтому я отмечаю это как ответ. Очень признателен за помощь! - person DanK; 11.05.2015