2016-02-09 3 views
0

Я работаю над улучшением производительности одного из запросов моей базы данных (MySQL). Все дело в том, что я сделал, у меня все еще низкая производительность. Перед моими изменениями Query запускается в 9.54, и после изменения некоторых индексов производительность повышается до 5.67s.медленный запрос с индексированными таблицами Mysql

Это мой запрос:

Query_time: 5,343565 Lock_time: 0,000302 Rows_sent: 100005 Rows_examined: 200017

Код:

SET timestamp=1455032448; 
SELECT 
    id, 
    description, 
    unit_price, 
    (SELECT coalesce(sum(quantity),0) from si_invoice_items where product_id = si_products.id) as qty_out , 
    (SELECT coalesce(sum(quantity),0) from si_inventory where product_id = si_products.id) as qty_in , 
    (SELECT coalesce(reorder_level,0)) as reorder_level , 
    (SELECT qty_in - qty_out) as quantity, 
    (SELECT (CASE WHEN enabled = 0 THEN 'Disabled' ELSE 'Enabled' END)) AS enabled 
FROM 
    si_products 
WHERE 
    visible = 1 
    AND domain_id = '1' 
ORDER BY 
    description asc; 

Это информация индекс всех таблиц :

Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type 
si_products 0 PRIMARY 1 id A 100005 NULL NULL BTREE 
si_products 0 PRIMARY 2 domain_id A 100005 NULL NULL BTREE 
si_products 1 unit_price 1 unit_price A 10000 NULL NULL YESBTREE 
si_products 1 description 1 id A 100005 NULL NULL BTREE 
si_products 1 description 2 description A 100005 15 NULL BTREE 

Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment 
si_inventory 0 PRIMARY 1 domain_id A NULL NULL NULL BTREE 
si_inventory 0 PRIMARY 2 id A 0 NULL NULL BTREE 
si_inventory 1 product_id 1 product_id A NULL NULL NULL BTREE 
si_inventory 1 quantity 1 quantity A NULL NULL NULL BTREE 

Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment 
si_invoice_items 0 PRIMARY 1 id A NULL NULL NULL BTREE 
si_invoice_items 0 PRIMARY 2 invoice_id A 7 NULL NULL BTREE 
si_invoice_items 1 unit_price 1 unit_price A NULL NULL NULL YES BTREE 
si_invoice_items 1 quantity 1 quantity A NULL NULL NULL BTREE 
si_invoice_items 1 product_id 1 product_id A NULL NULL NULL YES 

Любое предложение будет оценено.

С моего последнего изменения улучшить немного

Query_time: 3,723339 Lock_time: 0,000254 Rows_sent: 100005 Rows_examined: 200024 rows_affected: 0

SET timestamp=1455037952; 
SELECT 
    A.id, 
    A.description, 
    A.unit_price, 
    (SELECT coalesce(sum(B.quantity),0) from si_invoice_items B JOIN si_products A ON B.product_id = A.id) as qty_out , 
    (SELECT coalesce(sum(C.quantity),0) from si_inventory C JOIN si_products A ON C.product_id = A.id) as qty_in , 
    (SELECT coalesce(A.reorder_level,0)) as reorder_level , 
    (SELECT qty_in - qty_out) as quantity, 
    (CASE WHEN A.enabled = 0 THEN 'Disabled' ELSE 'Enabled' END) AS enabled 
FROM 
    si_products A 
WHERE 
    A.visible = 1 
    AND A.domain_id = '1' 

ORDER BY 
    description asc; 

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

Это результат вашего подхода:

# Query_time: 4.041339 Lock_time: 0.000245 Rows_sent: 100005 Rows_examined: 200029 
# Rows_affected: 0 
SET timestamp=1455045101; 
SELECT p.id, p.description, p.unit_price, 
    COALESCE(invoice.quantity,0) as qty_out, 
    COALESCE(inventory.quantity,0)as qty_in, 
    coalesce(p.reorder_level,0) as reorder_level , 
    (select qty_in - qty_out) as quantity 

    FROM si_products p 
    LEFT JOIN (
      SELECT SUM(quantity) quantity, product_id 
       FROM si_invoice_items 
       GROUP BY product_id 
    ) invoice ON p.id = invoice.product_id 
    LEFT JOIN (
      SELECT SUM(quantity) quantity, product_id 
       FROM si_inventory 
       GROUP BY product_id 
    ) inventory ON p.id = inventory.product_id 
WHERE p.visible = 1 
    AND p.domain_id = '1' 

ORDER BY    
     description asc; 
+0

Вы выполняете коррелированные подзапросы. вы ничего не можете сделать, чтобы улучшить ситуацию, поскольку каждый из этих запросов выполняется для каждой найденной строки родительской таблицы. например вы не используете один запрос, вы используете n * 5 запросов, где 'n' - количество записей в родительском запросе. –

+0

Точно я согласен с тобой. Последнее, что я сделал, это заменить предложение where с помощью JOIN и улучшить его до 3.6. –

+0

Если бы это был я, я бы уложил этот запрос и начал с некоторых операторов CREATE и INSERT и желаемого результата. – Strawberry

ответ

1

Вы можете реорганизовать этот запрос, чтобы устранить коррелированные подзапросы (вложенные ВЫБИРАЕТ в вашем ЗЕЬЕСТЕ). Если вы замените эти взаимосвязанные подзапросы с подзапросами JOINed, ваша производительность может улучшиться.

Похоже, вам нужны сводки ваших таблиц si_invoice_items и si_inventory.

Вы можете получить эти сводки с подзапросов, как это:

   SELECT SUM(quantity) quantity, product_id 
       FROM si_invoice_items 
       GROUP BY product_id 

и

   SELECT SUM(quantity) quantity, product_id 
       FROM si_inventory 
       GROUP BY product_id 

Вы можете рассматривать эти два подзапроса в качестве виртуальных таблиц, и присоединить их к вашей si_products таблице. Обратите внимание, что вам нужно использовать LEFT JOIN, потому что возможно, что некоторые из ваших строк si_products не будут иметь соответствующие строки в других таблицах.

Нравится так.

SELECT p.id, p.description, p.unit_price, 
     COALESCE(invoice.quantity,0) qty_out, 
     COALESCE(inventory.quantity,0) qty_in, 
     etc, etc 
    FROM si_products p 
    LEFT JOIN (
       SELECT SUM(quantity) quantity, product_id 
       FROM si_invoice_items 
       GROUP BY product_id 
     ) invoice ON p.id = invoice.product_id 
    LEFT JOIN (
       SELECT SUM(quantity) quantity, product_id 
       FROM si_inventory 
       GROUP BY product_id 
     ) inventory ON p.id = inventory.product_id 
WHERE p.visible = 1 
    AND p.domain_id = '1' 
ORDER BY p.description asc; 

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

Редактировать Вы извлекаете более 100 тыс. Строк с этим запросом. Это очень большой набор результатов и займет какое-то время независимо от того, что.

Вы можете сделать подзапросы summary (GROUP BY) быстрее, указав составные индексы в столбцах (product_id, quahtity) в ваших таблицах инвентаря и счета.

Вы можете сделать выбор записей из таблицы продуктов немного быстрее, указав составной индекс на столбцах (visible, domain_id).

Но ваша прикладная программа все еще должна измельчить сто тысяч строк.

+0

Спасибо Олли проверил запрос с вашим предложением, но, по-видимому, не улучшил время отклика. Посмотрите результаты. # Query_time: 4.041339 Lock_time: 0.000245 Rows_sent: 100005 Rows_examined: 200029. –

+1

Ollie, у меня есть предложение, чтобы ваши внутренние предварительно агрегированные запросы также присоединились к таблице «p», чтобы вы могли квалифицировать видимый и domain_id тоже. В противном случае вы суммируете ВСЕ записи для ВСЕЙ видимой (или нет) и домены. Если это сокращено квалификационным объединением, вы можете получить более эффективный ответ, особенно если эти запросы обрабатывают записи 1MIL из его ограниченного результата в 100 000 для всего домена и видимого статуса. – DRapp

0

Я не мог найти лучшие результаты, чем запрос:

Query_time: 3,174778 Lock_time: 0,000250 Rows_sent: 100005 Rows_examined: 200024

rows_affected: 0

SET timestamp=1455157468; 
SELECT 
    A.id, 
    A.description, 
    A.unit_price, 
    (SELECT coalesce(sum(B.quantity),0) from si_invoice_items B JOIN si_products A ON B.product_id = A.id) as qty_out , 
    (SELECT coalesce(sum(C.quantity),0) from si_inventory C JOIN si_products A ON C.product_id = A.id) as qty_in , 
    (SELECT coalesce(A.reorder_level,0)) as reorder_level , 
    (SELECT qty_in - qty_out) as quantity, 
    (CASE WHEN A.enabled = 0 THEN 'Disabled' ELSE 'Enabled' END) AS enabled 
FROM 
    si_products A 
WHERE 
    A.visible = 1 
    AND A.domain_id = '1' 

ORDER BY 
    description asc; 
0

Из-за того, сколько вы (Rows_sent: 100005), запрос не может быть очень быстрым.

Любой из этих показателей, вероятно, поможет некоторые из них:

INDEX(visible, domain_id, description) 
INDEX(domain_id, visible, description) 

Первые 2 поля могут помочь с фильтрацией; третий должен предотвратить необходимость «filesort» для ORDER BY.

Не используйте подзапрос, если выражения достаточно. Например:

(SELECT (CASE WHEN enabled = 0 THEN 'Disabled' ELSE 'Enabled' END)) AS enabled 

->

IF(enabled, 'Enabled', 'Disabled') AS enabled 

Для дальнейшего обсуждения, пожалуйста, предоставьте SHOW CREATE TABLE и EXPLAIN SELECT....

Смежные вопросы