В запросе у меня есть вычисляемое поле, основанное на нескольких критериях, которые необходимо сопоставить одновременно.
IF progress = 0
IF start<now() AND end>now() THEN "delay"
IF start<now() AND end<now() THEN "overdue"
IF progress BETWEEN 0 AND 1
IF start<now() THEN "advanced"
IF end<now() THEN "overdue"
IF end>now() AND end*progress>=now() THEN "in progress"
IF end>now() AND end*progress<now() THEN "delayed"
IF progress=1 THEN "completed"
Я смотрю, как установить этот набор условий с точки зрения производительности. Должен ли я изменить внешний IF (тот, что касается поля прогресса) на CASE?
Если производительность является основным фактором, я бы рекомендовал определить сгенерированный столбец как столбец STORED
. Тогда производительность выражения влияет только на вставку/обновление строки, а не на чтение вычисленного значения. Виртуальный сгенерированный столбец оценивает выражение при каждом чтении. Сохраненный столбец оценивает выражение только один раз, когда вы вставляете/обновляете строку.
Если вы хотите проверить относительную производительность выражений, попробуйте использовать функцию MySQL BENCHMARK().
@BillKarwin Разве выбор не зависит от того, чаще ли вы читаете или пишете соответствующие колонки?
Значение расчета будет меняться в зависимости от значения сегодняшней даты. Поэтому нельзя хранить только при вставке/обновлении. То, что сегодня вовремя, завтра может оказаться задержанным
В любом случае вы не можете использовать NOW()
в выражении сгенерированного столбца. Возможно, вам придется использовать представление.
@Barmar, Да, вы правы, но число операций чтения обычно значительно превышает число операций записи.
Предполагая, что вы решите проблему перекрытия, о которой я упоминал выше, обычно следует отдавать предпочтение CASE, поскольку он прекращает проверку условий, как только одно из условий выполнено успешно. Ваш код будет проверять наличие progress=1
, даже если он уже нашел это progress=0
.
Кроме того, если вы остаетесь с IF, используйте конструкцию IF..ELSEIF..ELSE, если только не будет выполнено несколько условий.
@BillKarwin Сейчас я использую упрощенную версию этих условий, и теперь она работает безупречно. Почему ты говоришь, что я не могу?
Это в триггере или вы определяете его как сгенерированный столбец? Какой результат SELECT VERSION();
?
@BillKarwin Я использую это как часть обычного выбора, а не триггера. И я использую MySQL версии 8.3.0.
Хорошо, вы можете использовать NOW()
в выражении. Я допустил ошибку, полагая, что вы определяете выражение как сгенерированный столбец таблицы. Существуют ограничения на то, какие функции можно использовать в определениях созданных столбцов.
Синтаксис, который вы показываете в своем вопросе, не является допустимым синтаксисом для выражения MySQL IF(). Возможно, если вы покажете пример рабочего запроса.
Короткий ответ: разница в производительности между IF
и CASE
, вероятно, незначительна.
Длинный ответ: Ну, ваш код не будет работать в его нынешнем виде, но позвольте мне угадать, что вы намеревались сделать:
IF start<now() AND end>now() THEN "delay"
IF start<now() AND end<now() THEN "overdue"
Предположим, что start
обязательно меньше end
, измените на
IF (start<now()) THEN
IF (end>now()) THEN "delay" ELSE IF (end<now()) THEN "overdue"
или
IF (start<now()) THEN
CASE WHEN end>now() THEN "delay"
WHEN end<now() THEN "overdue"
END
end*progress
слишком запутанно для слов. Запишите IF или CASE, а затем упростите.
Я смотрю, как установить этот набор условий с точки зрения производительности. Должен ли я изменить внешний IF (тот, что касается поля прогресса) на CASE?
Это не имеет значения. Любая разница будет тривиальной. MySQL может даже реализовать их точно так же.
Поскольку это часть предложения select
, оно влияет только на уже выбранные строки. Процесс чтения, фильтрации и сортировки строк намного дороже. Гораздо важнее сосредоточить усилия по оптимизации на этих частях вашего запроса: where
, order by
, join
, group by
, ваших индексах и так далее.
Примечание. end — зарезервированное слово SQL; неясно, ссылаетесь ли вы на столбец или завершаете блок, и это может привести к трудно отлаживаемым ошибкам. Рассмотрите возможность использования соглашений *_at
и *_on
для отметок времени и дат соответственно. start_at
и end_at
для меток времени, start_on
и end_on
для дат.
BETWEEN
является инклюзивным, поэтому условия частично совпадают.CASE
можно использовать только при взаимоисключающих условиях. Поэтому, если вы измените это наCASE
, это не будет эквивалентно.