По какой причине подзапрос FROM не отображается в подзапросе WHERE? - PullRequest
0 голосов
/ 09 мая 2018

Рассмотрим:

SELECT DISTINCT student_id
FROM (SELECT  * FROM Grades WHERE dept_id = 'MT') T
WHERE grade = (SELECT MAX(grade) FROM T);

Oracle жалуется, что T в подзапросе WHERE не является существующей таблицей. Я знаю, что могу легко обойти это, используя WITH, но все же я хочу понять. Какое правило SQL регулирует этот случай и какова логика этого правила?

1 Ответ

0 голосов
/ 09 мая 2018

Я не уверен, как важна причина; Это так, и зная, почему это не изменится.

Поскольку SQL является декларативным, существуют правила, касающиеся области действия, порядка выполнения, порядка приоритета и т. Д. Правила, которые позволяют планировщику на основе затрат создавать план, который фактически будет выполняться. Одним из таких правил является то, что вы не можете оценить два независимых запроса в одном наборе. Даже если бы T была таблицей материалов, двойная ссылка на нее привела бы в план как два независимых набора.

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

Например, вы можете приобрести два комплекта из одного и того же выражения таким образом ...

WITH
  T AS
(
  SELECT * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT student_id
FROM T
WHERE grade = (SELECT MAX(grade) FROM T);

Или вы можете использовать оконные функции и позволить внутреннему движку определить, как наилучшим образом оценить все условия с минимальными затратами ...

SELECT
  *
FROM
(
  SELECT
    Grades.*,
    MAX(grade) OVER ()   AS max_grade
  FROM
    Grades
  WHERE
    dept_id = 'MT'
)
  T
WHERE
  grade = max_grade


1024 *
*

ОЧЕНЬ ДЛИННОЕ РЕДАКТИРОВАНИЕ: Субъективные и объективные аргументы против предложения


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

SELECT DISTINCT
  student_id
FROM
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
  newSetDefinition
WHERE
  grade = (SELECT MAX(grade) FROM newSetDefinition)

-----------------------------
Functionally Equivalent To...
-----------------------------

WITH
  newSetDefinition
AS
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT
  student_id
FROM
  newSetDefinition
WHERE
  grade = (SELECT MAX(grade) FROM newSetDefinition)


Это подразумевает, что следующее также должно работать ...

SELECT DISTINCT
  newSetDefinition.student_id
FROM
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
  newSetDefinition
INNER JOIN
(
  SELECT MAX(grade) AS maxGrade FROM newSetDefinition
)
  newSetSummary
    ON newSetSummary.maxGrade = newSetDefinition.grade

-----------------------------
Functionally Equivalent To...
-----------------------------

WITH
  newSetDefinition
AS
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT
  newSetDefinition.student_id
FROM
  newSetDefinition
INNER JOIN
(
  SELECT MAX(grade) AS maxGrade FROM newSetDefinition
)
  newSetSummary
    ON newSetSummary.maxGrade = newSetDefinition.grade

Пока все хорошо ...


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

SELECT
  *
FROM
(
  SELECT * FROM table WHERE something = 1
)
  smeg
INNER JOIN
(
  SELECT
    *
  FROM
  (
    SELECT * FROM table WHERE somethingElse = 2
  )
    smeg
  INNER JOIN
  (
    SELECT MAX(id) AS maxID FROM smeg
  )
    smegSummary
      ON smegSummary.maxID = smeg.ID
)
  smegSubSet
    ON smegSubSet.parentID = smeg.ID

-----------------------------
Functionally Equivalent To...
-----------------------------

WITH
   smeg AS
(
  SELECT * FROM table WHERE something = 1
)
SELECT
  *
FROM
  smeg
INNER JOIN
(
  WITH
    smeg AS
  (
    SELECT * FROM table WHERE something = 1
  )
  SELECT
    *
  FROM
    smeg
  INNER JOIN
  (
    SELECT MAX(id) AS maxID FROM smeg
  )
    smegSummary
      ON smegSummary.maxID = smeg.ID
)
  smegSubSet
    ON smegSubSet.parentID = smeg.ID

Хорошо, так что все в порядке, просто немного неопрятно. CTE помогают избежать необходимости глубокого вложения, поэтому наличие вложенного синтаксиса для CTE «грязно», но даже это субъективная мера.

Когда вы видите «набор ссылок», вы обращаетесь к внешним запросам, пока не найдете набор с таким псевдонимом, и если ни один из них не найден, используйте обычные правила; CTES, затем таблицы / представления в текущей схеме, затем таблицы / представления в текущей базе данных, но другая схема, все с учетом разрешений и т. Д.

Прекрасные, довольно стандартные правила определения объема.


Но следующий сценарий более объективно проблематичен ...

SELECT
  *
FROM
(
  SELECT * FROM smeg WHERE something = 1
)
  smeg

В текущем ANSI-SQL это нормально, если есть таблица с именем smeg.

В AlwaysLearning-SQL это циклическая ссылка. «Ближайшее» определение для smeg - это внешний запрос. Это «переопределяет» любые таблицы или представления с именем smeg. Итак, внутренний запрос теперь выбирается из ... самого себя ...

Есть аргумент, чтобы сказать "просто позвольте этому вызвать ошибку круговой ссылки".

Но это нарушает обратную совместимость.

Представьте себе, если Oracle добавил эту функциональность в v13? Все неожиданные запросы, которые раньше работали, начинают вызывать ошибки циклических ссылок? Зачем? Чтобы некоторые подзапросы работали как CTE, при условии, что это полезно / удобно? To make some aspects of life "convenient" we broke some of your queries.

Произошло нарушение обратной совместимости. Но только когда выгоды далеко перевешивают последствия.

В этом случае все, что , что может быть сделано с вашим предложением, может быть сделано с CTE. И CTE были добавлены без нарушения привычного поведения. И (субъективно / возможно) CTE могут сделать это более структурированным, более понятным, простым для чтения, простым в отладке и т. Д.

Лично я очень рад, что никто еще не сломал некоторые запросы для реализации какой-то очень нишевой функциональности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...