У меня есть такой запрос:
select a.id from a join b on ( a.id = b.my_a ) join ....
where
( /* complex and expensive conditional */ )
AND
(( /* conditional #1 */ )
OR ( /* conditional #2 */ )
OR ( /* conditional #3 */))
Я бы хотел, чтобы запрос возвращал что-то вроде:
select a.id, conditional_1_eval_value, conditional_2_eval_value, conditional_3_eval_value from a join b on ( a.id = b.my_a ) join ....
where
( /* complex and expensive conditional */ )
AND
(( /* conditional #1 */ )
OR ( /* conditional #2 */ )
OR ( /* conditional #3 */))
где conditional_1_eval_value
, conditional_2_eval_value
и conditional_3_eval_value
установлены значение TRUE, FALSE, NULL. NULL, указывающий, что условие не было оценено.
Таким образом, набор результатов может быть:
1, FALSE, NULL, TRUE ( condition_1, condition_3 were evaluate, condition_2 was not)
2, NULL, TRUE, TRUE ( condition_2, condition_3 were evaluate, condition_1 was not)
3, TRUE, FALSE, FALSE (all were evaluated)
condition_1
, condition_2
, condition_3
являются сложными, связанными с коррелированными подзапросами и группировкой.
EDIT:
Нам нужно записать, какое условие вызвало возвращаемую строку. Нам не нужно знать все причины, по которым строка была возвращена. Поэтому во второй строке примера результатов достаточно знать, что conditional_2
и conditional_3
были истинными. Не зная, что значение conditional_1
не имеет значения.
Достаточно знать, что было выполнено хотя бы одно условие и какое это условие было.
Очевидно, я мог бы сделать это с помощью UNION следующим образом:
select a.id, TRUE, NULL, NULL from a join b on ( a.id = b.my_a ) join ....
where
( /* complex and expensive conditional */ )
AND
( /* conditional #1 */ )
UNION
select a.id, NULL, TRUE, NULL from a join b on ( a.id = b.my_a ) join ....
where
( /* complex and expensive conditional */ )
AND
( /* conditional #2 */ )
UNION
select a.id, NULL, NULL, TRUE from a join b on ( a.id = b.my_a ) join ....
where
( /* complex and expensive conditional */ )
AND
( /* conditional #3 */)
Но это будет означать, что:
Используя CASE в select, который дублирует каждое условие с 1 по 3, вы избегаете 3 раза общее условие. Однако сложность условного 1-3 такова, что это может быть невозможно.
Использование select в предложении FROM было бы неудобным и может быть невозможным, потому что FROM SELECT не может быть коррелированным запросом. Я не уверен, что могу построить полезный некоррелированный запрос.
Сохраненные процедуры будут работать. Однако это будет первой такой хранимой процедурой и значительно увеличит нашу сложность развертывания.
Выполнение оценки conditional_1
, conditional_2
, conditional_3
в Java-коде. Это то, что мы сейчас делаем, и оно запускает sloooooooow. Множество данных, передаваемых, когда база данных предназначена для фильтрации набора результатов, не должна делать это в java!
Кто-нибудь?
Я также должен добавить, что приветствую ответы, в которых говорится, что эта проблема не может быть решена. Знание того, что проблема не может быть решена, сэкономит мне время, пытаясь решить ее с помощью строго SQL.
Если бы мне пришлось выбирать, я бы наклонился, чтобы узнать, как выглядит хранимая процедура mysql.
Итак, если вы хотите добровольно заявить, как будет выглядеть хранимая процедура mysql, это было бы здорово.
Чтобы добиться того, что вы пытаетесь сделать, почему бы не использовать хранимые функции для сложных условий?
Это приведет к выражению select, например:
select func1(arg1, arg2, ...), func2(arg1, arg2, ...), rest_of_select_columns
from table1, table2
where (complex1 logic)
OR func1(arg1, arg2, ....) = 1 /* return to give true */
OR func2(arg1, arg2, ....) = 1
Примечания:
Вы правы, что вытащить все данные обратно в java и выполнить ваши условия будет собакой.
Однако, вы только реальный выбор для UNION 3 разных запросов. Из-за того, как работает реляционный движок, нет способа вытащить то, что попало на запись.
Фрэнки, я не вижу, как я могу выразить проблему в MySQL (или фактически в любом SQL). Тем не менее, я столкнулся с аналогичной сложной проблемой большого набора данных пару лет назад 1.
Основываясь на накопленном опыте, я могу дать некоторые идеи о том, как ускорить оценку:
1 на самом деле ограничение заключалось в том, чтобы иметь быстрое вычисление по требованию в наборе записей ~ 1M.
Вы можете просто выбрать все данные относительно результатов, затем выполнить эту условную логику в своем клиентском коде, работающем с результирующим набором?
Если вы хотите уменьшить повторный набор сложных выражений, поместите выражения в производную таблицу подзапроса:
SELECT t.*
FROM (
SELECT a.*, b.*, ...
/* conditional #1 */ AS c1,
/* conditional #2 */ AS c2,
/* conditional #3 */ AS c3
FROM a JOIN b ON (a.id = b.my_a)
...) AS t
WHERE /* ...other conditions... */
AND ((c1) OR (c2) OR (c3));
Это также решение другого вопроса: "Как я могу использовать псевдонимы столбцов в условиях WHERE?"