Как я могу справиться с трудоемким SQL? - PullRequest
2 голосов
/ 25 августа 2010

У нас есть таблица с 6 миллионами записей, а затем у нас есть SQL, для запроса которого требуется около 7 минут.Я думаю, что SQL больше не может быть оптимизирован.

Время запроса заставляет нашу weblogic выдать исключение максимального застрявшего потока.

Есть ли какие-либо рекомендации для меня, чтобы решить эту проблему?

Ниже приведен запрос, но мне трудно его изменить,

SELECT * FROM  table1 
WHERE trim(StudentID) IN ('354354','0') 
AND concat(concat(substr(table1.LogDate,7,10),'/'),substr(table1.LogDate,1,5)) 
       BETWEEN '2009/02/02' AND '2009/03/02' 
AND TerminalType='1' 
AND RecStatus='0' ORDER BY StudentID, LogDate DESC, LogTime

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

LogDate была определена как строка, и форматом является мм / дд / гггг, поэтому нам нужно подстроковать и конкатенировать ее, чем мы можем использовать между ... и ... Я думаю, что этоздесь сложно оптимизировать.

Ответы [ 10 ]

7 голосов
/ 25 августа 2010

Скорее всего, этот запрос выполняет полное сканирование файла, потому что вы ГДЕ условия вряд ли сможете использовать какие-либо индексы.

Является ли LogDate полем даты или текстовым полем? Если это поле даты, то не делайте подстроки и конкататы. Просто скажите «LogDate между« 2009-02-02 »и« 2009-02-03 »или любым другим диапазоном дат. Если он определен как текстовое поле, вам следует серьезно подумать о переопределении его в поле даты. является текстом и записывается в мм / дд / гггг, тогда ваш ORDER BY ... LOGDATE DESC не даст полезных результатов, если даты охватывают более одного года.)

Необходимо ли выполнять обрезку на StudentID? Гораздо лучше очистить ваши данные перед тем, как поместить их в базу данных, чем пытаться очищать их каждый раз, когда вы их извлекаете.

Если LogDate определена как дата, и вы можете обрезать данные на входе, то создайте индексы для одного или обоих полей, и время запроса должно резко упасть.

Или, если вы хотите быстрое и грязное решение, создайте индекс для «trim (studentid)».

Если это не поможет, дайте нам больше информации о ваших таблицах и индексах.

4 голосов
/ 25 августа 2010
SELECT * ... WHERE trim(StudentID) IN ('354354','0')

Если это нормальная конструкция, тогда вам нужен индекс на основе функции . Потому что без этого вы заставляете сервер БД выполнять полное сканирование таблицы.

Как правило, вам следует избегать максимально возможного использования функций в предложении WHERE. trim(StundentID), substr(table1.LogDate,7,10) не позволяют серверам БД использовать какой-либо индекс или применять к запросу какую-либо оптимизацию. Старайтесь максимально использовать собственные типы данных, например, DATE вместо VARCHAR для LogDate. StudentID должно также правильно управляться в клиентском программном обеспечении, например, обрезать данные перед INSERT / UPDATE.

3 голосов
/ 25 августа 2010

Если ваша база данных поддерживает это, вы можете попробовать материализованное представление .

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

2 голосов
/ 25 августа 2010

Но время запроса заставляет нашу weblogic выдавать исключение max застрявшего потока.

Если запрос занимает 7 минут и не может быть выполнен быстрее, вы должны прекратить выполнять этот запрос по-настоящему-время.Можете ли вы изменить свое приложение для запроса кэшированной таблицы результатов, которую вы периодически обновляете?

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

Я обновил запрос, не могли бы вы дать мне несколько советов?

Те,манипуляции со строками делают индексацию практически невозможной.Вы уверены, что не можете хотя бы избавиться от «обрезки»?Есть ли действительно лишние пробелы в реальных данных?Если это так, вы можете сузить только один student_id, что должно значительно ускорить процесс.

Вам нужен составной индекс для (student_id, log_date), и, надеюсь, сложное условие log_date все еще можно решить с помощьюсканирование диапазона индекса (для данного идентификатора студента).

2 голосов
/ 25 августа 2010

Без какой-либо дополнительной информации о том, какой запрос вы выполняете и какие индексы вы используете, трудно дать какую-либо конкретную информацию.

Но вот несколько общих советов.

  1. Убедитесь, что вы используете индексы для столбцов, по которым вы часто фильтруете / упорядочиваете.
  2. Если это только определенный запрос, который слишком медленный, то, возможно, вы можете помешать себе выполнить этот запрос с помощьюавтоматическая генерация результатов при изменении базы данных.Например, вместо count() вы обычно можете хранить счет где-нибудь.

Попробуйте удалить trim() из запроса, автоматически вызывая trim() для ваших данных до / во время вставкиэто в таблицу.Таким образом, вы можете просто использовать индекс, чтобы найти StudentID.

Кроме того, фильтр date должен быть встроен в вашу базу данных.Не зная, для какой базы данных это может быть сложнее, но что-то вроде этого, вероятно, должно работать: LogDate BETWEEN '2009-02-02' AND '2009-02-02'

Если вы также добавите индекс для всех этих столбцов вместе (т.е. StudentID, LogDate, TerminalType, RecStatus и EmployeeID, чем должно быть молниеносно.

1 голос
/ 26 августа 2010

Сказано много, ваша проблема в поле даты. Вам определенно нужно изменить дату со строкового поля на собственный тип даты. Если это унаследованное поле, которое используется в вашем приложении именно таким образом, вы все равно можете создать to_date(logdate, 'DD/MM/YYYY') индекс на основе функции, который преобразует вашу «строковую» дату в «дату» и позволяет быстро упомянуть between поиск без изменения данных таблицы.

Это должно значительно ускорить процесс.

1 голос
/ 25 августа 2010

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

В вашем запросе следующая часть concat(concat(substr(table1.LogDate,7,10),'/'), substr(table1.LogDate,1,5)) BETWEEN '2009/02/02' AND '2009/02/02'

это слишком смешно. МЕЖДУ '2009/02/02' И '2009/02/02' ??Чувак, что вы пытаетесь сделать?

Можете ли вы опубликовать здесь структуру вашей таблицы?

И 6 миллионов записей не так уж и много.

0 голосов
/ 26 августа 2010

Если StudentID равен char (обычно это причина использования trim()), вы можете добиться лучшей производительности, заполнив переменные, а не обрезая поле, как показано ниже (при условии, что StudentID равен char(10)):

StudentID IN (lpad('354354',10),lpad('0',10))

Это позволит использовать индекс StudentID, если он существует.

0 голосов
/ 25 августа 2010

Ваша основная проблема в том, что ваш запрос обрабатывает все как строку.

Если LogDate является датой БЕЗ компонента времени, вы хотите что-то вроде следующегоимеет компонент времени, а у SearchDate нет компонента времени, тогда что-то вроде этого.(.99999 установит время на 1 секунду до полуночи)

SELECT * FROM  table1 
WHERE  StudentID  IN (:SearchStudentId,:StudentId0) 
AND  table1.LogDate BETWEEN :SearchDate AND :SearchDate+0.99999 
AND TerminalType='1' 
AND RecStatus='0' 
ORDER BY EmployeeID, LogDate DESC, LogTime

Обратите внимание на использование переменных связывания для параметров, которые изменяются между вызовами.Это не сделает запрос намного быстрее, но это «лучшая практика».

В зависимости от языка вызова может потребоваться добавить TO_DATE и т. Д., Чтобы преобразовать входящую переменную связывания в тип Date.

0 голосов
/ 25 августа 2010

Учитывая небольшую предоставленную вами информацию, я догадываюсь, что следующий пункт дает нам подсказку:

     ... WHERE trim(StudentID) IN ('354354','0') 

Если у вас есть большое количество записей с неопознанным студентом (то есть studentID = 0), индекс для studentID будет очень несбалансированным.

Из 6 миллионов записей у скольких студенческий идентификатор = 0?

...