2013-11-20 3 views
13

ПроблемойSQL - Отношения между подзапросом и внешней таблицей

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

Следующее утверждение работает надлежащим образом:

SELECT t1.* 
FROM table1 t1, 
INNER JOIN table2 t2 
     on t1.id = t2.id   
     and t2.date = (SELECT max(date) 
         FROM table2 
         WHERE id = t1.id) --This subquery has access to t1 

К сожалению table2 иногда имеет повторяющиеся записи, поэтому мне нужно агрегировать t2 первым, прежде чем я присоединяюсь к его t1. Однако, когда я пытаюсь обернуть его в подзапрос, чтобы выполнить эту операцию, внезапно механизм SQL больше не сможет распознать внешнюю таблицу.

SELECT t1.* 
FROM table1 t1, 
INNER JOIN (SELECT * 
      FROM table2 t2 
      WHERE t1.id = t2.id    --This loses access to t1 
       and t2.date = (SELECT max(date) 
          FROM table2 
          WHERE id = t1.id)) sub on t1.id = sub.id 
          --Subquery loses access to t1 

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

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

Полезные Ссылки

  • Я нашел это фантастическое описание порядка, в котором положения выполняются в SQL Server: (INNER JOIN ON vs WHERE clause). Я использую Oracle, но я думаю, что это будет стандартно по всем направлениям. Существует четкий порядок определения предложения (с первым FROM), поэтому я думаю, что любое предложение, появившееся дальше в списке, будет иметь доступ ко всей ранее обработанной информации. Я могу только предположить, что мой второй запрос каким-то образом изменяет порядок, чтобы мой подзапрос оценивался слишком рано?

  • Кроме того, я нашел подобный вопрос задал (Referencing outer query's tables in a subquery ), но в то время как вход был хорошим, они никогда не объяснили, почему он не может делать то, что он делает, и просто дал альтернативные решения своей проблемы. Я пробовал свои альтернативные решения, но это вызывает другие проблемы. А именно, этот подзапрос с ссылкой на дату является фундаментальным для всей операции, поэтому я не могу избавиться от нее.

Вопросы

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

  • Сказанное, если то, что я пытаюсь сделать, не может быть сделано, каков наилучший способ рефакторинга первого запроса для устранения дублирования? Должен ли я дважды ссылаться на таблицу1 (со всем необходимым дублированием)? Или есть (возможно) лучший способ решить эту проблему?

Заранее благодарен!

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

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

------ ОБНОВЛЕНИЕ ------

Так что я побежал это разработчик собрата, и он был одним из возможных объяснений того, почему мой подзапрос теряет доступ к t1. Поскольку я заключу этот подзапрос в круглую скобку, он считает, что этот подзапрос оценивается до того, как будет оценена моя таблица t1. Это определенно объясняет ошибку «ORA-00904:« t1 ».« Id »: неверный идентификатор, который я получил. Это также предполагает, что, подобно арифметическому порядку операций, добавление parens в оператор дает ему приоритет в определенных оценках клаузулы. Я все равно буду любить эксперта, чтобы взвесить, если они согласятся/не согласны, что является логичным объяснением того, что я вижу здесь.

+1

Выведенная таблица не может быть скоррелирована. Он должен стоять один. Хотя вы можете присоединиться к нему. вы можете использовать 'APPLY', где вам нужна какая-то коррелированная производная таблица. –

+0

Martin .... искренне, спасибо! Основываясь на вашем комментарии, я смог сделать небольшое дополнительное исследование и обнаружил, что мой запрос на самом деле не извлекает данные, как я изначально думал. Также ваш совет по применению Apply кажется очень применимым (хотя для меня синтаксис немного отличается, поскольку я использую Oracle). Очень ценю совет - на самом деле, если вы представите его в качестве ответа, я буду отмечать его как правильный. – DanK

ответ

6

Так что я понял это, основываясь на комментарии, которые сделали выше Мартин Смит (БЛАГОДАРЯ MARTIN!), И я хотел убедиться, что я поделился своим открытием для всех, кто поездки по этому вопросу.

Технические соображения

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

Это на самом деле довольно неэффективный способ отходил данные, как это вновь запускает подзапрос для каждой строки внешней таблицы.По этой причине я буду искать способы устранения этих типов подзапросов в моем коде:

Мое второе заявление с другой стороны использовало то, что называется видом рядных в Oracle также известный как производной таблицы в SQL Server:

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

Почему то, что я делал, был глуп

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

Обход

Наконец, стоит отметить, что Мартин предложил довольно умный, но в конечном счете неэффективный способ сделать то, что я пытался сделать. Нанести заявление является патентованной функцией SQL Server, но она позволяет разговаривать с объектами за пределами вашей производной таблицы:

Аналогично эта функциональность доступна в Oracle через другой синтаксис:

В конечном счете я г чтобы переоценить весь мой подход к этому запросу, что означает, что мне придется перестроить его с нуля (верьте или нет, я не создал это чудовищность изначально - клянусь!). Огромное спасибо всем, кто прокомментировал - это определенно превзошло меня, но все входные данные помогли мне на правильном пути!

+0

+1 Размышление о них как о встроенных взглядах объясняет проблему хорошо. [Более поздние версии Oracle действительно поддерживают APPLY, хотя] (http://technology.amis.nl/2013/06/26/oracle-database-12c-joining-and-outer-joining-with-collections/) –

+0

Спасибо за информация ... Я все еще использую 11G, чтобы функциональность была для меня новостью! – DanK

+0

приятный ответ, но все еще неспособный получить острый край между встроенным представлением и коррелированным запросом, когда рассматривать запрос как это и когда? –

0

Как насчет следующего запроса:

SELECT t1.* FROM 
(
    SELECT * 
    FROM 
    (
    SELECT t2.id, 
    RANK() OVER (PARTITION BY t2.id, t2.date ORDER BY t2.date DESC) AS R 
    FROM table2 t2 
) 
    WHERE R = 1 
) sub 
INNER JOIN table1 t1 
ON t1.id = sub.id 
+0

Спасибо, что указали это - я отредактировал мой пример выше. Я не натягиваю t2. * Во втором запросе.Это была опечатка для этой записи – DanK

+0

Нет, она дает ту же ошибку компиляции. Он не может распознать t1, если Inner Join ссылается на подзапрос, не указывая явно из таблицы1. Специфическая ошибка ORA-00904: «t1». «Id»: неверный идентификатор – DanK

+0

* @ DanK *, я отредактировал свой ответ, это работает? – Linger

-1

В вашем втором примере вы пытаетесь передать ссылку t1 вниз 2 уровня .. вы не можете сделать это, вы можете только передать его вниз на 1 уровень (поэтому 1-й работает). Если вы дадите лучший пример того, что вы пытаетесь сделать, мы также можем помочь вам переписать ваш запрос.

+0

Это то, что я и подумал, но если я заменил (SELECT max (date) FROM table2 WHERE id = t1.id)) с одной датой, он все равно не будет компилироваться. Проблема заключается в внутреннем соединении непосредственно в подзапросе, который ссылается на внешнюю таблицу. Но почему? – DanK

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