Нужна помощь в оптимизации запросов SQL, чтобы избежать лишних математических операций в WHERE и SELECT - PullRequest
0 голосов
/ 18 марта 2011

* Привет всем, я работаю над запросом и не уверен, как сделать так, чтобы он обрабатывался как можно быстрее и с минимальной избыточностью, насколько это возможно. Я действительно надеюсь, что кто-то там может помочь мне придумать хороший способ сделать это.

Заранее спасибо за помощь! *

Хорошо, вот то, что у меня есть, как я могу объяснить. Я упростил таблицы и математику, чтобы просто понять то, что я пытаюсь понять.

По сути, у меня небольшая таблица, которая никогда не меняется и всегда будет содержать только 50 тыс. Записей:

Values_Table

ID      Value1     Value2
1          2           7
2          2           7.2
3          3           7.5
4          33          10
….50000     44       17.2

И пара таблиц, которые постоянно меняются и являются довольно большими, например, с потенциалом до 5 миллионов записей:

Flags_Table

Index      Flag1    Type
1           0       0
2             0       1
3             1       0
4            1       1
….5,000,000 1       1

Users_Table

Index           Name       ASSOCIATED_ID
1               John           1
2               John           1
3               Paul           3
4               Paul           3
….5,000,000 Richard         2

Мне нужно связать все 3 таблицы вместе. Большинство результатов, которые, вероятно, когда-либо будут возвращены из небольшого стола, находятся где-то около 100 результатов. Большие таблицы объединяются в индексе, а затем они соединяются с Values_Table ON Values_Table.ID = Users_Table.ASSOCIATED_ID…. Эта часть достаточно проста.

Для меня становится сложным то, что мне нужно как можно быстрее вернуть список, ограниченный 10 результатами, где математически оперируются value1 и value2, чтобы вернуть new_ value, где new_value меньше 10 и результат сортируется по этому new_value и любым другим, где нужные мне заявления могут быть применены к флагам. Мне нужно уметь двигаться по пределу. EG LIMIT 0,10 / 11,10 / 21,10 и т.д ...

В последующем (или таком же, если возможно) запросе мне нужно получить 10 лучших счетчиков всех типов, которые соответствовали этому критерию до применения ограничения.

Так, например, я хочу присоединиться ко всем этим и вернуть что-нибудь, где Value1 + Value2 <10 И мне также нужно количество. </p>

Итак, что я хочу:

Index      Name           Flag1 New_Value
1           John           0           9
2           John           0           9
5000000 Richard         1           9.2

Второй ответ будет:

ID (not index)      Count
    1                  2
    2                  1

Я попробовал это несколькими способами и в итоге придумал следующий довольно уродливый запрос:

SELECT INDEX, NAME, Flag1, (Value1 * some_variable + Value2) as New_Value
FROM Values_Table
JOIN Users_Table ON ASSOCIATED_ID = ID
JOIN Flags_Table ON Flags_Table.Index = Users_Table.Index
WHERE (Value1 * some_variable + Value1) < 10
ORDER BY New_Value
LIMIT 0,10

А затем для подсчета:

SELECT ID, COUNT(TYPE) as Count, (Value1 * some_variable + Value2) as New_Value
FROM Values_Table
JOIN Users_Table ON ASSOCIATED_ID = ID
JOIN Flags_Table ON Flags_Table.Index = Users_Table.Index
WHERE (Value1 * some_variable + Value1) < 10
GROUP BY TYPE
ORDER BY New_Value
LIMIT 0,10

Важно иметь возможность фильтровать различные флаги и тому подобное в моем предложении WHERE; комментировать это может показаться глупым, но я упоминаю об этом, потому что из того, что я увидел, можно было бы быстрее использовать оператор HAVING, но я не верю, что в некоторых случаях это сработает, в зависимости от того, что я хочу использовать в предложении WHERE. фильтровать против.

А при фильтрации с использованием таблицы флагов:

SELECT INDEX, NAME, Flag1, (Value1 * some_variable + Value2) as New_Value
FROM Values_Table
JOIN Users_Table ON ASSOCIATED_ID = ID
JOIN Flags_Table ON Flags_Table.Index = Users_Table.Index
WHERE (Value1 * some_variable + Value1) < 10 AND Flag1 = 0
ORDER BY New_Value
LIMIT 0,10

... отфильтрованный счет:

SELECT ID, COUNT(TYPE) as Count, (Value1 * some_variable + Value2) as New_Value
FROM Values_Table
JOIN Users_Table ON ASSOCIATED_ID = ID
JOIN Flags_Table ON Flags_Table.Index = Users_Table.Index
WHERE (Value1 * some_variable + Value1) < 10 AND Flag1 = 0
GROUP BY TYPE
ORDER BY New_Value
LIMIT 0,10

Это прекрасно работает, но приходится выполнять математику несколько раз для каждой строки, и у меня возникает неприятное ощущение, что она также выполняет математику несколько раз в одной и той же строке таблицы Values_table. Я думал, что сначала я должен получить только действительные ответы из Values_table, а затем соединить их с другими таблицами, чтобы сократить обработку; с тем, как SQL оптимизирует вещи, хотя я не был уверен, может ли он этого не делать. Я знаю, что мог бы использовать предложение HAVING, чтобы выполнить математику только один раз, если бы я делал это таким образом, но я не уверен, как бы я тогда лучше всего к этому присоединился.

Мои вопросы:

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

Кажется, это должно быть до боли просто, но я просто упускаю что-то глупое.

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

Спасибо всем за помощь в этом и, пожалуйста, дайте мне знать, если мне нужно что-то прояснить здесь!

** Чтобы уточнить вопрос, я не могу использовать 3-й столбец с предварительно рассчитанными значениями, потому что на самом деле математика гораздо сложнее, чем сложение, я просто упростил ее для иллюстрации.

Ответы [ 3 ]

1 голос
/ 19 марта 2011

У вас есть запрос на сравнение? Обычно не получается перехитрить оптимизатор. Если у вас приемлемая производительность при запуске запроса, вы можете увидеть, на что тратится дополнительная работа (на что указывает чтение с диска, использование кэша и т. Д.), И сосредоточиться на этом.

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

Избыточная математика, как правило, в порядке - что вредит работе диска. Я никогда не видел запроса, который требовал бы сокращения работы ЦП на чистых вычислениях.

0 голосов
/ 18 марта 2011

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

0 голосов
/ 18 марта 2011

Соберите свои результаты и поместите их во временную таблицу

SELECT * into TempTable FROM (SELECT INDEX, NAME, Type, ID, Flag1, (Value1 + Value2) as New_Value
               FROM Values_Table
               JOIN Users_Table ON ASSOCIATED_ID = ID
               JOIN Flags_Table ON Flags_Table.Index = Users_Table.Index
WHERE New_Value < 10)
ORDER BY New_Value
LIMIT 0,10

Возвращаемый результат для первого запроса

SELECT INDEX, NAME, Flag1, New_Value 
FROM TempTable

Возврат результатов для подсчета типов

Select ID, Count(Type)
FROM TempTable
GROUP BY TYPE
...