MySQL: использование подзапроса в предложении SELECT и сколько раз он выполняется?

Если у меня есть запрос, например:

SELECT firstName, age, COALESCE(studentStatus, SELECT status FROM Statuses WHERE default = true) FROM Student;

Сколько раз выполняется запрос внутри COALESCE? Я думаю, поскольку это не зависит от внешнего запроса, его следует выполнять только один раз. Правда ли это и как это работает? Кэширует ли MySQL где-нибудь результат?


person oneCoderToRuleThemAll    schedule 26.03.2020    source источник
comment
Во-первых, ваш запрос даже не будет выполнен, если этот подзапрос на Statuses вернет более одной строки. Предполагая, что он не возвращает более одной строки, тогда да, MySQL просто оценит его один раз и где-нибудь кэширует результат. В этом случае это будет некоррелированный подзапрос, и он будет вести себя как константа.   -  person Tim Biegeleisen    schedule 26.03.2020
comment
не храните возраст, он устареет. возраст – это разница между датой рождения и текущей датой. Используйте EXPLAIN, чтобы увидеть план выполнения запроса. MySQL может выполнять этот подзапрос для каждой строки, возвращаемой внешним запросом. Подзапрос должен иметь гарантию, что будет возвращено не более одной строки, либо агрегата в списке SELECT ( SELECT MAX(status), либо LIMIT 1. Лично я бы избегал подзапроса в списке выбора... и выполнял соединение с производной таблицей / встроенный вид, ... и избежать двусмысленности вокруг ненужного подзапроса в списке SELECT   -  person spencer7593    schedule 26.03.2020
comment
@ spencer7593 Можете ли вы привести пример достижения вышеуказанного с помощью соединения? В приведенном выше примере учащийся может иметь один из status из столбца Statuses или null. Как заменить нуль статусом по умолчанию?   -  person oneCoderToRuleThemAll    schedule 26.03.2020


Ответы (1)


Используйте EXPLAIN, чтобы увидеть план выполнения запроса. MySQL мог бы выполнять этот подзапрос для каждой строки, возвращаемой внешним запросом, это было бы допустимо. Поскольку это не коррелированный подзапрос, вполне вероятно, что MySQL оптимизирует его, выполнив подзапрос только один раз, кэшируя результат и возвращая кэшированное значение для каждой строки.

Обратите внимание, что если этот подзапрос когда-либо вернет более одной строки, то весь оператор выдаст ошибку. Подзапрос в списке SELECT должен включать какую-то гарантию того, что он не вернет более одной строки. Либо агрегат в списке SELECT (например, SELECT MAX(status) ), либо предложение LIMIT 1.


ПРИМЕЧАНИЕ:

Обычно не рекомендуется хранить атрибут age. Возраст человека определяется как разница в годах между датой рождения и текущей датой. Если нам нужен возраст «по состоянию на» определенную дату, мы используем эту дату вместо текущей даты.


Моим личным предпочтением было бы избегать выполнения подзапроса в списке SELECT и выполнять присоединение к встроенному представлению, которое возвращает статус по умолчанию.


В: Можете ли вы привести пример достижения вышеуказанного с помощью соединения?

Вот пример:

SELECT t.firstname                              AS `first_name`
     , TIMESTAMPDIFF(YEAR,t.dob,DATE(NOW()))    AS `age_yrs` 
     , IFNULL(t.studentstatus,s.default_status) AS `status`
  FROM `Student` t
 CROSS
  JOIN ( SELECT MAX(d.status) AS default_status
           FROM `Statuses` d
          WHERE d.default = TRUE
       ) s
 ORDER
    BY t.firstname
person spencer7593    schedule 27.03.2020