Как получить результаты условных оценок ГДЕ в наборе результатов? - PullRequest
0 голосов
/ 27 июня 2009

ПРОБЛЕМА

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

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

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

  1. общий «сложный и дорогой условный» оценивается 3 раза.
  2. что все условные выражения оцениваются, даже если другое условное условие уже удовлетворяло OR.
  3. был бы кошмар обслуживания, гарантирующий, что 3 копии общего сложного запроса идентичны (это можно решить, создав 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, это было бы замечательно.

Ответы [ 5 ]

2 голосов
/ 27 июня 2009

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

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

Примечания:

  1. SQL не поддерживает логический тип данных, поэтому true, false, поскольку результаты из функции невозможны. Следовательно, 0,1 возвращаемое значение.
  2. В зависимости от вашей версии mySQL вы можете сделать функции DETERMINISTIC, что может привести к некоторым улучшениям производительности.
1 голос
/ 27 июня 2009

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

Тем не менее, у вас есть только реальный выбор - объединить 3 разных запроса. Из-за того, как работает реляционный движок, нет возможности извлечь то, что «попало» на запись.

1 голос
/ 27 июня 2009

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

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

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

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

0 голосов
/ 27 июня 2009

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

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 голосов
/ 27 июня 2009

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

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