SQL-связь между вложенным запросом и внешней таблицей
мне нужно лучше понять правила о том, когда я могу ссылаться на внешнюю таблицу в подзапросе и когда (и почему) это неуместный запрос. Я обнаружил дублирование в Oracle SQL-запрос, я пытаюсь рефакторинг, но я бегу в вопросы, когда я пытаюсь включить мой таблицы в подзапрос с группировкой.
следующий оператор работает соответствующим образом:
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 engine больше не может распознать внешнюю таблицу.
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, но я думаю, что это будет стандарт по всем направлениям. Существует четкий порядок клаузулы оценка (с первого), поэтому я думаю, что любой пункт, происходящий дальше по списку, будет иметь доступ ко всей ранее обработанной информации. Я могу только предположить, что мой 2-й запрос каким-то образом изменяет порядок, чтобы мой подзапрос оценивался слишком рано?
кроме того, я нашел похожий вопрос (таблицы внешнего запроса в подзапросе ) но хотя вход был хорошим, они никогда не объясняли, почему он не мог сделать то, что он делает и просто дает альтернативные решения своей проблемы. Я пробовал их альтернативные решения, но это вызывает у меня другие проблемы. А именно, этот подзапрос со ссылкой на дату является фундаментальным для всей операции, поэтому я не могу избавиться от него.
вопросы
Я хочу понять, что я сделал здесь... Почему мой первоначальный подзапрос может видеть внешнюю таблицу, но не после того, как я оберну весь оператор в подзапрос?
тем не менее, если то, что я пытаюсь сделать, не может быть сделано, каков наилучший способ рефакторинга первого запроса для устранения дублирования? Должен ли я ссылаться на таблицу 1 дважды (со всем необходимым дублированием)? Или есть (возможно) лучший способ решения этой проблемы?
спасибо заранее!
------ редактировать------
как некоторые предположили, эти запросы выше на самом деле не являются запрос я рефакторинг, но пример проблемы, с которой я сталкиваюсь. Запрос, с которым я работаю, намного сложнее, поэтому я не решаюсь опубликовать его здесь, поскольку я боюсь, что это собьет людей с пути.
------ обновление------
поэтому я запустил это другим разработчиком, и у него было одно возможное объяснение того, почему мой подзапрос теряет доступ к t1. Поскольку я обертываю этот подзапрос в скобки, он думает, что этот подзапрос оценивается перед моим таблица t1 оценивается. Это определенно объясняет 'ORA-00904:"t1"."id": ошибка недопустимого идентификатора, которую я получаю. Это также предполагает, что, как и арифметический порядок операций, добавление parens в оператор дает ему приоритет в определенных оценках предложения. Я все равно хотел бы, чтобы эксперт взвесил, если они согласны / не согласны, что это логическое объяснение того, что я вижу здесь.
3 ответов
поэтому я понял это на основе комментария, который Мартин Смит сделал выше (спасибо, Мартин!) и я хотел убедиться, что я поделился своим открытием для всех, кто сталкивается с этой проблемой.
Технические Вопросы
во-первых, это, безусловно, поможет, если я использую правильную терминологию для описания моей проблемы: мое первое утверждение выше использует коррелируется subquery:
- http://en.wikipedia.org/wiki/Correlated_subquery
- http://www.programmerinterview.com/index.php/database-sql/correlated-vs-uncorrelated-subquery/
на самом деле это довольно неэффективный способ возврата данных при повторном выполнении подзапроса для каждой строки во внешней таблице. По этой причине я собираюсь искать способы устранения этих подзапросов в моем код:
мое второе утверждение, с другой стороны, использовало то, что называется встроенные вида в Oracle также известный как производная таблица в SQL Сервер:
- http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries007.htm
- http://www.programmerinterview.com/index.php/database-sql/derived-table-vs-subquery/
встроенное представление / производная таблица создает временное неназванное представление в начале запроса, а затем обрабатывает его как другую таблицу до завершения операции. Потому что компилятору необходимо создать временное представление, когда он видит на этих подзапросах на линии FROM,эти подзапросы должны быть полностью автономны без ссылок за пределами подзапроса.
почему то, что я делал, было глупо
то, что я пытался сделать в этой второй таблице, по существу, создавало представление, основанное на двусмысленной ссылке на другую таблицу, которая была вне знания моего утверждения. Это было бы похоже на попытку ссылаться на поле в таблице, которое вы явно не указали в запросе.
решение
наконец, стоит отметить, что Мартин предложил довольно умный, но в конечном счете неэффективный способ выполнить то, что я пытался сделать. Инструкция Apply-это проприетарная функция SQL Server, но она позволяет разговаривать с объектами за пределами производной таблицы:
дополнительно это функциональность доступна в Oracle через другой синтаксис:
в конечном итоге я собираюсь переоценить весь свой подход к этому запросу, что означает, что мне придется перестроить его с нуля (верьте или нет, я не создавал это чудовище изначально - я клянусь!). большое спасибо всем, кто прокомментировал - это определенно ставило меня в тупик, но все входные данные это помогло мне встать на правильный путь!
Как насчет следующего запроса:
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
во втором примере вы пытаетесь передать ссылку t1 вниз на 2 уровня.. вы не можете этого сделать, вы можете только передать его на 1 уровень (вот почему 1-й работает). Если вы приведете лучший пример того, что вы пытаетесь сделать, мы также поможем вам переписать ваш запрос.