Подтвердить что ты не робот

SQL - взаимосвязь между SubQuery и внешней таблицей

Проблема

Мне нужно лучше понять правила о том, когда я могу ссылаться на внешнюю таблицу в подзапросе и когда (и почему) это ненадлежащий запрос. Я обнаружил дублирование в запросе 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). Я использую Oracle, но я думаю, что это будет стандартно по всем направлениям. Существует четкий порядок определения предложения (с первым FROM), поэтому я думаю, что любое предложение, появившееся дальше в списке, будет иметь доступ ко всей ранее обработанной информации. Я могу только предположить, что мой второй запрос каким-то образом изменяет порядок, чтобы мой подзапрос оценивался слишком рано?

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

Вопросы

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

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

Спасибо заранее!

------ ------ РЕДАКТИРОВАТЬ

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

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

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

4b9b3361

Ответ 1

Итак, я понял это на основе комментария, сделанного Мартином Смитом выше (СПАСИБО МАРТИН!), и я хотел убедиться, что поделился своим открытием с кем-либо другим, кто путешествует по этой проблеме.

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

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

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

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

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

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

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

Обход

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

Подобным образом эта функциональность доступна в Oracle через различный синтаксис:

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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