2017-01-13 8 views
-1

У меня есть 4 таблицы в минимальной точки зрения здесь:MySQL оптимизации подзапросов

продаж:

id 
has_discount 
discount_is_percentage 
discount_amount 
**sale_date_time** 
**order_status** 

Sales_items:

id 
**sales_id** 
has_discount 
discount_is_percentage 
discount_amount 
**product_id** (This can sometimes be null) 
price_inc_vat_per_item 
quantity 
vat_rate 
is_removed 

Sales_payments:

id 
**sales_id** 
payment_amount 
payment_change 
payment_method 

Продукты:

id 
product_name 

У меня есть запрос, который рассчитывает скидку «на лету» и сообщает об этом. Это отлично работает, когда общее количество записей осталось ниже 100-200 тыс. Но по мере увеличения числа время, затраченное на это очень медленно. Я думаю, это из-за подзапроса, который я использую. Любой может пролить свет на это, пожалуйста. В каждой таблице есть client_id и outlet_id, которые отличают их от других пользователей в системе.

В настоящее время таблицы имеют 1-3 миллиона строк, и у клиента, у которого есть вопрос, есть 300k-600k. Запрос занимает 30 секунд. Для других с небольшим количеством строк можно получить его даже в подсеансы. Индексы - звезды со звездами. Как можно улучшить запрос для получения желаемых результатов? Запрос у меня сейчас:

SELECT DATE_FORMAT(CONVERT_TZ(sales.sale_date_time,'UTC','Europe/London'), 
       '%l%p') as title, count(*) as total_sales, SUM(sales_items.quantity 
        ) as total_quantities, 
     SUM(sales_items.price_before_line_discount) as price_before_line_discount, 
     SUM(sales_items.price_before_line_discount-sales_items.line_discount) as price_after_line_discount, 
     SUM(sales_items.vat_rated_sales) as vat_rated_sales_before_discount, 
     SUM(sales_items.zero_rated_sales) as zero_rated_sales_before_discount, 
     SUM(sales_items.total_vat_only) as total_vat_only_before_discount, 
     SUM(sales_payments.payment_taken) as payment_taken, SUM(sales_items.line_discount) as total_line_discount, 
     SUM(sales_payments.payment_cash) as payment_cash, SUM(CASE WHEN sales.has_discount=1 
       AND sales.discount_is_percentage=0 THEN sales.discount_amount WHEN sales.has_discount=1 
       AND sales.discount_is_percentage=1 THEN ((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100) WHEN sales.has_discount=0 THEN 0 END 
      )as total_sales_discount, 
     SUM(CASE WHEN sales.has_discount=1 THEN CASE WHEN discount_is_percentage=0 THEN (sales_items.vat_rated_sales*sales.discount_amount)/(sales_items.price_before_line_discount-sales_items.line_discount) WHEN discount_is_percentage=1 THEN (sales_items.vat_rated_sales*((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100))/(sales_items.price_before_line_discount-sales_items.line_discount) END ELSE 0 END)as vat_rated_sales_discount, 
     SUM(CASE WHEN sales.has_discount=1 THEN CASE WHEN discount_is_percentage=0 THEN (sales_items.zero_rated_sales*sales.discount_amount)/(sales_items.price_before_line_discount-sales_items.line_discount) WHEN discount_is_percentage=1 THEN ((sales_items.zero_rated_sales*((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100))/(sales_items.price_before_line_discount-sales_items.line_discount)) END ELSE 0 END)as zero_rated_sales_discount, 
     SUM(CASE WHEN sales.has_discount=1 THEN CASE WHEN discount_is_percentage=0 THEN (sales_items.total_vat_only*sales.discount_amount)/(sales_items.price_before_line_discount-sales_items.line_discount) WHEN discount_is_percentage=1 THEN (sales_items.total_vat_only*((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100))/(sales_items.price_before_line_discount-sales_items.line_discount) END ELSE 0 END)as total_vat_only_discount 
    FROM `sales` 
    left join 
    (
     SELECT sales_id, SUM(quantity) as quantity, SUM(price_inc_vat_per_item*quantity) AS price_before_line_discount, 
       SUM(CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN discount_amount WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)*discount_amount/100) WHEN has_discount=0 THEN 0 END 
        )as line_discount, 
       SUM(CASE WHEN vat_rate>0 THEN CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN ((price_inc_vat_per_item*quantity)-discount_amount) WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100)) WHEN has_discount=0 THEN (price_inc_vat_per_item*quantity) END ELSE 0 END 
        )as vat_rated_sales, 
       SUM(CASE WHEN vat_rate=0 THEN CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN ((price_inc_vat_per_item*quantity)-discount_amount) WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100)) WHEN has_discount=0 THEN (price_inc_vat_per_item*quantity) END ELSE 0 END 
        )as zero_rated_sales, 
       SUM(CASE WHEN vat_rate>0 THEN CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN ((price_inc_vat_per_item*quantity)-discount_amount)-((price_inc_vat_per_item*quantity)-discount_amount)/(1+(vat_rate/100)) WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100))-((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100))/(1+(vat_rate/100)) WHEN has_discount=0 THEN (price_inc_vat_per_item*quantity)-(price_inc_vat_per_item*quantity)/(1+(vat_rate/100)) END ELSE 0 END 
        )as total_vat_only 
      FROM sales_items 
      WHERE client_id='0fe26d93-775f-440c-a119-13cbcb6cbc0c' 
       AND is_removed=0 
      GROUP BY sales_id 
    ) as sales_items ON `sales`.`id` = `sales_items`.`sales_id` 
    left join 
    (
     SELECT sales_id, SUM(payment_amount-payment_change) payment_taken, 
       SUM(CASE WHEN payment_method='CASH' THEN (payment_amount-payment_change) ELSE 0 END) as payment_cash 
      FROM sales_payments 
      WHERE client_id='0fe26d93-775f-440c-a119-1396c36cbc0c' 
      GROUP BY sales_id 
    ) as sales_payments ON `sales`.`id` = `sales_payments`.`sales_id` 
    WHERE `sales`.`client_id` = '0fe26d93-775f-440c-a119-1396c36cbc0c' 
     and `sales`.`outlet_id` = 'd5b74bdf-5cef-4455-bf99-13cbcb6cbc0c' 
     and `sales`.`order_status` = 'COMPLETED' 
     and `sale_date_time` >= '2016-01-28 00:00:00' 
     and `sale_date_time` <= '2016-11-28 23:59:00' 
    GROUP BY HOUR(CONVERT_TZ(sales.sale_date_time,'UTC','Europe/London')) 
    ORDER BY `sale_date_time` ASC 

UPDATE:

Чтобы ответить на вопросы от @ рик-Джеймсе

  • мне нужно отсортировать его по sale_date_time, который является полем даты и времени. Группе необходимо сообщать по часам. У него также есть дни, месяц-год и т. Д., Зависит от запрашиваемого периода.
  • Пришлось использовать UUID из-за конструкции. Вся БД составляет около 8 ГБ, где эти четыре таблицы имеют большую часть. Длина индекса больше фактического размера данных, так как у меня было много внешних ограничений.

Это на Амазонке Аврора с ОЗУ 15 ГБ.

продаж Таблица: Индекс 0,5 Гб данных 1.3GB

продаж Товары: 1.3GB данных 3.2GB Index

продаж Оплата: данных 0,5 Гб 1.1GB Индекс

Все таблицы сортировки utf8_unicode_ci.

  • Использование Aurora 5.6, которое является MySQL 5.6. Вот объяснение select.

ID таблицы SELECT_TYPE типа possible_keys этим ключам key_len реф строки фильтруются дополнительные

1 ПЕРВИЧНЫЕ продаж исх sales_client_id_outlet_id_foreign, sales_client_id_index, sales_outlet_id_index, sales_sale_date_time_index, sales_order_status_index sales_client_id_index 108 Const 5352 Использование индекса состояния; Использование где; Использование временных; Использование FileSort

1 ПЕРВИЧНЫЙ исх 108 MyDB.sales.id 10

1 ПЕРВИЧНЫЙ исх 108 MyDB.sales.id 10

3 ПРОИЗВОДНЫЕ sales_payments реф sales_payments_client_id_outlet_id_foreign, sales_payments_client_id_index sales_payments_client_id_outlet_id_foreign 108 Const 5092 Использование индекса состояния; Использование где; Использование временных; Использование FileSort

2 ПРОИЗВОДНЫЕ sales_items réf sales_items_client_id_outlet_id_foreign, sales_items_client_id_index sales_items_client_id_outlet_id_foreign 108 Const 13340 Использование индекса состояния; Использование где; Использование временных; Использование FileSort

2 Химпродукты eq_ref PRIMARY, products_id_unique PRIMARY 108 MyDB.sales_items.product_id 1

  • Может быть будет выглядеть в магазине результат в БД и получить оттуда. Единственная проблема заключается в том, что старые заказы могут быть изменены, и общая сумма должна быть перестроена, если это произойдет.

Любой другой способ переписать запрос, чтобы получить желаемый результат?

ответ

0
  • Когда ORDER BY излишне отличается от GROUP BY, необходим дополнительный проход.
  • UUIDs ужасно неэффективны, когда данные больше, чем могут быть кэшированы в ОЗУ. Насколько велики таблицы? Какова ценность `innodb_buffer_pool_size? Сколько у вас RAM?
  • LEFT JOIN (SELECT ...) ужасно неэффективен, по крайней мере, до 5.6. Пожалуйста, предоставьте EXPLAIN SELECT ..., чтобы узнать, оптимизирован ли он. Какую версию ты используешь?
  • Еще хуже то, что LEFT JOIN (SELECT ...) LEFT JOIN (SELECT ...). Добавлено: Так как я не вижу «авто-ключ», это плохо. Это заставляет меня задаться вопросом, действительно ли это MySQL 5.6.
  • Создание и ведение «Сводной таблицы» может быть окончательным ответом. Вероятно, у него будет PRIMARY KEY, включая client_id, outlet_id, order_status и sale_HOUR.
  • Выполняет ли какой-либо подзапрос самостоятельно? Если да, начните отдельный вопрос, чтобы сосредоточиться только на подзапросе. Предоставьте вывод от SHOW CREATE TABLE; есть много деталей, отсутствующих в вашем описании таблиц - индексы, типы данных, размеры, сортировки и т. д. Добавлено: Еще нужно; есть еще кое-что, что нужно проверить. Возможное решение: CREATE TEMPORARY TABLE с каждым из двух LEFT JOIN SELECTs; затем используйте их.
+0

См. Обновленный вопрос. – James

+0

см. Добавлен ...... –

+0

Извините, он имеет на производных таблицах. SO просто лишил это из вложенного кода.Я попытаюсь использовать временную таблицу, чтобы убедиться, что это улучшится. – James

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

  • Нет связанных вопросов^_^