Как получить результаты условных оценок ГДЕ в наборе результатов?

0

ПРОБЛЕМА

У меня есть такой запрос:

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 */)

Но это будет означать, что:

  • общий "сложный и дорогой условный" оценивается 3 раза.
  • что все условные выражения оцениваются, даже если другое условие уже удовлетворяет OR.
  • был бы кошмар обслуживания, гарантирующий идентичность трех копий общего сложного запроса (нормально разрешимо путем построения sql в коде и копирования общей строки), но это означает, что я нарушу другой внутренний стандарт для всех sql, не являющихся встроенный в java, но находящийся в XML файле с видимыми для DBA)

Используя CASE в select, который дублирует каждое условие с 1 по 3, вы избегаете 3 раза общее условие. Однако сложность условного 1-3 такова, что это может быть невозможно.

Использование select в предложении FROM было бы неудобным и может быть невозможным, потому что FROM SELECT не может быть коррелированным запросом. Я не уверен, что могу построить полезный некоррелированный запрос.

Сохраненные процедуры будут работать. Однако это будет первой такой хранимой процедурой и значительно увеличит нашу сложность развертывания.

Выполнение оценки conditional_1, conditional_2, conditional_3 в Java-коде. Это то, что мы сейчас делаем, и оно запускает sloooooooow. Множество данных, передаваемых, когда база данных предназначена для фильтрации набора результатов, не должна делать это в java!

Рекомендации по решению?

Кто-нибудь?

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

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

Итак, если вы хотите добровольно заявить, как будет выглядеть хранимая процедура mysql, это было бы здорово.

  • 0
    Основываясь на ответах (полу-ожидаемых, не относящихся к категории супер-спасателей), я попытаюсь еще больше сократить запрос и посмотреть, что изменится. Добавлю больше когда узнаю больше ... спасибо.
Теги:

5 ответов

2

Чтобы добиться того, что вы пытаетесь сделать, почему бы не использовать хранимые функции для сложных условий?

Это приведет к выражению 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 

Примечания:

  • SQL не поддерживает тип данных Boolean, поэтому true, false, поскольку результаты из функции невозможны. Следовательно, 0,1 возвращаемых значения.
  • В зависимости от вашей версии mySQL вы можете выполнять функции DETERMINISTIC, что может привести к некоторым улучшениям производительности.
  • 0
    Таким образом, эти функции будут действовать так же, как CASE концептуально, но с лучшей производительностью? Это позволило бы избежать проблемы дублирования условных выражений путем инкапсуляции условных выражений в функциях. [Хранимые процедуры были перечислены под неоптимальными решениями в вопросе]
  • 0
    Частично это будет действовать как случай. Использование DETERMINISTIC (если mySQL делает это «правильно») и если условия являются «детерминированными», запрос будет выполняться значительно быстрее. В Oracle (и я предполагаю, что mySQL - то же самое), когда Query Engine «видит» ранее «готовый» список аргументов, он заменяет предыдущий результат (сохраняя много, когда один и тот же список аргументов встречается в более чем одной строке) ,
Показать ещё 2 комментария
1

Вы правы, что вытащить все данные обратно в java и выполнить ваши условия будет собакой.

Однако, вы только реальный выбор для UNION 3 разных запросов. Из-за того, как работает реляционный движок, нет способа вытащить то, что попало на запись.

  • 0
    чувак, ты не должен это говорить :-)
1

Фрэнки, я не вижу, как я могу выразить проблему в MySQL (или фактически в любом SQL). Тем не менее, я столкнулся с аналогичной сложной проблемой большого набора данных пару лет назад 1.

Основываясь на накопленном опыте, я могу дать некоторые идеи о том, как ускорить оценку:

  • Я бы подумал о переключении на другой механизм базы данных (заменив текущий или просто скопировав в него данные). Я бы использовал Oracle, поскольку я знаю его возможности с точки зрения оптимизации запросов. Другой вариант - использовать встроенный SQL-движок для приближения данных ближе к месту вычисления.
  • У меня будет второй взгляд на текущую оценку на основе Java. Возможно, настройка размеров пакетов запросов, связанных с параллельными запросами различных таблиц, связанных с проблемой, и использование потокового подхода.
  • Если бы у меня было достаточное количество памяти для моего Java-кода, я бы подумал о том, чтобы хранить/кэшировать все данные в памяти все время, если вычисление должно выполняться часто.
  • Или будет искать способ вывести общие части условий, чтобы получить некоторую скорость, разделив их между условиями.

1 на самом деле ограничение заключалось в том, чтобы иметь быстрое вычисление по требованию в наборе записей ~ 1M.

  • 0
    Нет. Условия не являются переменными. Но обработка каждой строки изменяется в зависимости от того, какое из трех условий выполнено. Буду редактировать вопрос.
  • 0
    Мы стартап. Решение должно быть достигнуто в течение нескольких часов. Не несколько дней. Прямо сейчас это выглядит так, как будто сохраненная функция лидирует. Естественно, я постараюсь уменьшить / консолидировать запрос, но я должен исходить из предположения, что я не смогу в назначенное время. Re: Оценка на основе Java; это то, что мы пытаемся заменить.
0

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

  • 0
    Нет. Условные_1, Условные_2 и условные_3 нетривиальны. Выполнение этого в коде стоит дорого и может привести к переносу большого количества данных.
0

Если вы хотите уменьшить повторный набор сложных выражений, поместите выражения в производную таблицу подзапроса:

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?"

  • 0
    у вас есть ОТ (SELECT / * условный # 1 / AS c1,) ... так что же заменить / условный # 1 * / с?
  • 0
    Также, как указано в вопросе ОТ ВЫБОРОВ, должны быть некоррелированные запросы. Если я смогу предложить запрос FROM SELECT, то это будет мое предпочтительное решение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню