2016-06-04 5 views
2

Я пытаюсь настроить свой запрос, но я не могу идти дальше. есть ли возможность настроить этот запрос? особенно SUUM-запрос.Как увеличить производительность суммирования столбца

ИНДЕКС: db_prices.date

Пример исходного запроса:

SELECT 
db_villas.id, 
db_villas.title1, 
db_specials.id AS sid, 
db_specials.title1 AS stitle, 
db_cities.name AS cityName, 
db_counties.name AS countyName, 
db_assets.path, 
db_villas.bathroom, 
db_villas.bedroom, 
db_villas.guest, 
db_prices.date, 
(SELECT SUM(db_prices.price) FROM db_prices WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" AND db_prices.villaId=db_villas.id) AS price 
FROM 
db_villas 
INNER JOIN db_cities ON db_villas.cityId = db_cities.id 
LEFT OUTER JOIN db_specials ON db_villas.specialId = db_specials.id 
INNER JOIN db_counties ON db_counties.cityid = db_cities.id AND db_villas.countyId = db_counties.id 
INNER JOIN db_assets ON db_assets.guid = db_villas.guid 
INNER JOIN db_villafacilities ON db_villafacilities.villaId = db_villas.id 
INNER JOIN db_prices ON db_prices.villaId = db_villas.id 
WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" AND db_assets.isMainImage=1 AND db_villas.minRent <= 7 
GROUP BY db_villas.id 
HAVING (SELECT COUNT(*) FROM db_prices WHERE date BETWEEN "2016-08-01" AND "2016-09-30" AND db_prices.isFree = 0 AND db_prices.villaId = db_villas.id)=0 

запрос на уровне выше выполняется в 1,2 секунды.

Когда я удалить

(SELECT SUM(db_prices.price) FROM db_prices WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" AND db_prices.villaId=db_villas.id) AS price 

суб время выполнения запросов снижается до 0,009 секунд.

Если я просто удалить эту часть

AND db_prices.villaId=db_villas.id 

из запроса к югу она по-прежнему выполнен в 0,009 секунд.

+0

Используйте EXPLAIN, чтобы просмотреть план выполнения ваших запросов. Он сообщает вам, какие части не используют правильный индекс. – jkavalik

ответ

3

MySQL (по состоянию против 5,7) имеет планировщик запросов без котлет, чтобы превратить ваш зависимый подзапрос

(SELECT SUM(db_prices.price) 
    FROM db_prices 
    WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" 
    AND db_prices.villaId=db_villas.id) AS price 

в подключаемый независимый подзапрос. Это означает, что планировщик запросов заканчивает выполнение этого запроса много раз, используя время выполнения. Поэтому вам нужно преобразовать его самостоятельно. Независимый подзапрос будет выглядеть следующим образом:

    SELECT villaId, 
         SUM(price) price, 
         SUM(CASE WHEN isFree = 0 THEN 1 ELSE 0 END) not_free_count 
        FROM db_prices 
        WHERE date BETWEEN '2016-08-01' AND '2016-09-30' 
        GROUP BY villaId 

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

SELECT db_villas.id, 
     db_villas.title1, etc etc, 
     price_summary.price 
    FROM db_villas 
    INNER JOIN db_cities ON db_villas.cityId = db_cities.id 
    LEFT OUTER JOIN db_specials ON db_villas.specialId = db_specials.id 
      etc etc. 
    LEFT JOIN (
       SELECT villaId, 
         SUM(price) price, 
         SUM(CASE WHEN isFree = 0 THEN 1 ELSE 0 END) not_free_count 
        FROM db_prices 
        WHERE date BETWEEN '2016-08-01' AND '2016-09-30' 
        GROUP BY villaId 
     ) price_summary ON db_villas.villaId = price_summmary.villaId 
WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" 
    AND etc etc 
    AND price_summary.not_free_count = 0 
GROUP BY db_villas.villaId 

Затем вам нужно составной индекс на db_prices (date, villaId, price, isFree) для оптимизации подзапросов. Вам также могут потребоваться индексы на некоторых других столбцах других таблиц.

Pro tip: Множество индексов с одним столбцом не заменяет сложные индексы при ускорении запросов. Индексирование множества столбцов по отдельности является обычным, и печально известным, противостоянием. Прочитайте это: http://use-the-index-luke.com/

Pro наконечник: Ваш запрос использует нестандартное расширение MySQL для GROUP BY. В версии MySQL вы, возможно, скоро, это перестанет работать, если вы не измените некоторые настройки сервера. Прочтите следующее: https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

0

Создать (композитный) индекс многоколоночного это решить бы ваши проблемы

Чтобы сделать это ударило ниже запрос

create index <some_name> on db_prices(date,villaId); 

Если вы столкнулись дополнительные проблемы опубликовать объяснить заявление для дальнейшего расследования

2

Для ускорения этого подзапроса:

(SELECT SUM(p.price) 
FROM db_prices p 
WHERE p.date BETWEEN '2016-08-01' AND '2016-09-30' AND 
     p.villaId = db_villas.id 
) AS price 

Y ou хотите индекс. Лучший индекс - это индекс покрытия с этими столбцами в следующем порядке: db_prices(villaId, date, price). Индекс покрытия включает все столбцы в подзапросе.

Столбец villaId должен быть первым, поскольку он имеет условие равенства; затем date, так как это также в статье where. Наконец, price находится в индексе, чтобы сделать обработку немного более эффективной - все столбцы находятся в индексе, поэтому движку не нужно искать значения на страницах данных.

+0

дата уже проиндексирована, но я изменяю индексы и ордера, как вы писали здесь. Но запрос, выполняемый за 1,1 секунды, нормальный? не можем ли мы пойти дальше? –

+0

выньте встроенный select из вашего основного оператора select, добавьте это как соединение, иначе он будет вызван для каждой строки вашего результирующего набора, а не только один раз, как если бы он был в соединении –

+0

@ EricLaboy. , , Как производительность этой версии сравнивается с версией Олли? –

1

Попробуйте сделать агрегат первый и присоединиться к нему со столом

SELECT 
db_villas.id, 
db_villas.title1, 
db_specials.id AS sid, 
db_specials.title1 AS stitle, 
db_cities.name AS cityName, 
db_counties.name AS countyName, 
db_assets.path, 
db_villas.bathroom, 
db_villas.bedroom, 
db_villas.guest, 
db_prices.date, 
pricesum 
FROM (SELECT db_prices.villaId, SUM(db_prices.price) as pricesum FROM db_prices WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" group by db_prices.villaId) as prices 
INNER JOIN db_villas ON prices.villaId = db_villas.id 
... 

Иногда это помогает.

EDIT исправлены некоторые ошибки копирования

+0

с этим временем выполнения трюка сокращено до 0,25 секунды :) Спасибо –

+0

не выглядел достаточно близко к подзапросу в заключении, а не к Олие Джонсу – Turo