Улучшение/оптимизация производительности запросов в PHP, MySQL, Zend

У меня есть накладные, инвойсы_элементы, заказ, заказ_элементы. Таблицы счетов и заказов содержат около 1 миллиона записей. Таблицы Invoices_items и Orders_items содержат более 2 миллионов записей. Таблица Items содержит 2 сотни тысяч записей. Теперь я хочу создать отчет на основе моего фильтра, такого как клиенты, категории товаров и многое другое.... Пожалуйста, обратитесь запросы.

Работает на PHP 5.6. MySql 5.7 и Apache2.

SELECT
  `si_items`.`item_id`
  , SUM(qty) AS `qty`
  , IFNULL(SUM(selling_price * (qty)), 0) AS `salestotal`
  , GROUP_CONCAT(si.id) AS `siso_id` 
  , MAX(si.date_transaction) AS `date_transaction`
FROM
  `invoice_items` AS `si_items`
  LEFT JOIN `invoice` AS `si`
    ON si.id = si_items.parent_id
  LEFT JOIN `items`
    ON si_items.item_id = items.id
WHERE (
    DATE_FORMAT(si.date_transaction, '%Y-%m-%d') BETWEEN '2019-01-01'
    AND '2019-02-15'
  )
  AND (si.approved = 1)
  AND (si.deleted = 0)
  AND (items.deleted = 0)
    GROUP BY `item_id`

     UNION

SELECT
  `so_items`.`item_id`
  , SUM(qty) AS `qty`
  , IFNULL(SUM(selling_price * (qty)), 0) AS `salestotal`
  , GROUP_CONCAT(so.id) AS `soso_id` 
  , MAX(so.date_transaction) AS `date_transaction`
FROM
  `order_items` AS `so_items`
  LEFT JOIN `order` AS `so`
    ON so.id = so_items.parent_id
  LEFT JOIN `items`
    ON so_items.item_id = items.id
WHERE (
    DATE_FORMAT(so.date_transaction, '%Y-%m-%d') BETWEEN '2019-01-01'
    AND '2019-02-15'
  )
  AND (so.approved = 1)
  AND (so.deleted = 0)
  AND (items.deleted = 0)
    GROUP BY `item_id`

Когда я выполнял этот запрос в течение 50 дней. Выполнение этого запроса заняло 1 минуту 20 секунд.

ИНДЕКСЫ добавлены в таблицы

Таблицы счетов и заказов

PRIMARY KEY (`id`),
KEY `account_id` (`account_id`),
KEY `approved` (`approved`),
KEY `deleted` (`deleted`),
KEY `finalised` (`finalised`),
KEY `rp_status` (`rp_status`),
KEY `sales_types_id` (`sales_types_id`),
KEY `account_type_id` (`account_type_id`),
KEY `company_id` (`company_id`),
KEY `date_transaction` (`date_transaction`)

Invoices_items & Order_items

PRIMARY KEY (`id`),
KEY `deleted` (`deleted`),
KEY `item_id` (`item_id`),
KEY `parent_id` (`parent_id`),
KEY `vat_id` (`vat_id`),
KEY `qty` (`qty`),

Объяснить запрос

Объяснить запрос

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

Показать создание таблиц

CREATE TABLE `invoice` (
  `id` char(36) NOT NULL,
  `reference` varchar(25) DEFAULT NULL,
  `company_id` char(36) DEFAULT NULL,
  `branch_id` char(36) DEFAULT NULL,
  `account_id` char(36) DEFAULT NULL,
  `contact_id` char(36) DEFAULT NULL,
  `transaction_type` varchar(10) DEFAULT NULL,
  `sales_types_id` int(11) DEFAULT '0',
  `quote_validity` int(11) DEFAULT '0',
  `delivery_method_id` int(11) DEFAULT '0',
  `sales_representative_id` int(11) DEFAULT '0',
  `account_type_id` char(36) DEFAULT NULL,
  `vat_exempted` tinyint(1) DEFAULT '0',
  `description` text,
  `finalised` tinyint(1) DEFAULT '0' COMMENT 'Not Yet finalised - status=1; Need Approval - status = 2; Approved - status = 3',
  `approved` tinyint(1) DEFAULT '0',
  `approved_user_id` int(11) DEFAULT '0',
  `default_sales_location_id` char(36) DEFAULT NULL COMMENT '0-Yes; 1-No',
  `generate_do` tinyint(1) DEFAULT '1',
  `generate_dn` tinyint(4) DEFAULT '1',
  `do_status` tinyint(1) DEFAULT '0',
  `cn_status` tinyint(1) DEFAULT '0',
  `rp_status` tinyint(1) DEFAULT '0',
  `dm_status` tinyint(1) DEFAULT '0',
  `currency_id` char(36),
  `exchange_rate_id` tinyint(1) DEFAULT '0',
  `exchange_rate` double DEFAULT '1',
  `date_transaction` datetime DEFAULT NULL,
  `date_created` datetime DEFAULT NULL,
  `date_modified` datetime DEFAULT NULL,
  `created_user_id` int(11) DEFAULT '0',
  `modified_user_id` int(11) DEFAULT '0',
  `deleted` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `account_id` (`account_id`),
  KEY `approved` (`approved`),
  KEY `branch_id` (`branch_id`),
  KEY `cn_status` (`cn_status`),
  KEY `created_user_id` (`created_user_id`),
  KEY `date_created` (`date_created`),
  KEY `deleted` (`deleted`),
  KEY `do_status` (`do_status`),
  KEY `finalised` (`finalised`),
  KEY `reference` (`reference`),
  KEY `rp_status` (`rp_status`),
  KEY `sales_types_id` (`sales_types_id`),
  KEY `account_type_id` (`account_type_id`),
  KEY `company_id` (`company_id`),
  KEY `date_transaction` (`date_transaction`),
  KEY `default_sales_location_id` (`default_sales_location_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `invoice_items` (
  `id` char(36) NOT NULL,
  `parent_id` char(36) DEFAULT NULL,
  `item_id` char(36) DEFAULT NULL,
  `qty` double DEFAULT '0',
  `cost_price` double DEFAULT '0',
  `list_price` double DEFAULT '0',
  `selling_price` double DEFAULT '0',
  `unit_price` double DEFAULT '0',
  `vat` double DEFAULT '0',
  `amount` double DEFAULT '0',
  `special_discount` double DEFAULT '0',
  `price_change_status` tinyint(1) DEFAULT '0',
  `remarks` text,
  `vat_id` int(11) DEFAULT '1',
  `stock_category_id` tinyint(2) DEFAULT '0' COMMENT '1: Stockable 2: Service',
  `is_giftitem` tinyint(1) DEFAULT '0' COMMENT '1: Gift Item 0: NO Gift',
  `item_type_status` tinyint(1) DEFAULT '0',
  `date_created` datetime DEFAULT NULL,
  `date_modified` datetime DEFAULT NULL,
  `created_user_id` int(11) DEFAULT '0',
  `modified_user_id` int(11) DEFAULT '0',
  `deleted` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `deleted` (`deleted`),
  KEY `item_id` (`item_id`),
  KEY `parent_id` (`parent_id`),
  KEY `stock_category_id` (`stock_category_id`),
  KEY `item_type_status` (`item_type_status`),
  KEY `vat_id` (`vat_id`),
  KEY `amount` (`amount`),
  KEY `qty` (`qty`),
  KEY `unit_price` (`unit_price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Я бы лично сказал, что это относится к codereview.stackexchange.com, попробуйте опубликовать это там.

Isaac 15.02.2019 11:04

Какие индексы у вас есть на этих таблицах?

Tim Fountain 15.02.2019 11:40

@Isaac, спасибо, я тоже написал.

rsm 15.02.2019 11:44

@TimFountain, я добавил индексы в пост.

rsm 15.02.2019 11:45

Пожалуйста, укажите все столбцы; мы не знаем, например, в какой таблице находится selling_price.

Rick James 16.02.2019 05:11

Столы ENGINE=InnoDB? (Почему бы просто не показать нам SHOW CREATE TABLE)

Rick James 16.02.2019 05:13

@RickJames, обновил таблицу создания шоу в посте. почти одинаковая структура для таблиц счетов и заказов

rsm 13.03.2019 08:29

@rsm - Спасибо. Я добавил к своему ответу. Имейте в виду, что «составные» индексы отличаются от индексов с одним столбцом. Кроме того, индексация флага редко бывает полезной.

Rick James 13.03.2019 16:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
8
75
1

Ответы 1

Не используйте LEFT JOIN, когда вы имеете в виду JOIN. В частности, за вступление в si.

WHERE (
    DATE_FORMAT(si.date_transaction, '%Y-%m-%d') BETWEEN '2019-01-01'
    AND '2019-02-15'
  )

-->

WHERE si.date_transaction >= '2019-01-01'
  AND si.date_transaction  < '2019-01-15'

так что индекс (см. ниже) может использовать этот столбец

WHERE  si.date_transaction ...
  AND (si.approved = 1)
  AND (si.deleted = 0)

Добавьте составной индекс:

INDEX(deleted, approved,   -- in either order
      date_transaction)    -- last

Внесите аналогичные изменения в so. Тогда давайте послушаем, как обстоят дела, и посмотрим, на что изменился EXPLAIN.

UUID

Остерегайтесь UUID, они громоздкие и медленные. Они особенно медленны, если вся таблица не может быть кэширована.

Я подозреваю, что у вас есть uuids, потому что я вижу CHAR(36).

Наличие CHARACTER SET utf8 означает, что используется 108 байт!. UUID может быть упакован в 16-байтовый BINARY(16). Это помогло бы с пространством (и, следовательно, со скоростью).

Но настоящая проблема с UUID связана со случайностью. Как только таблица становится огромной, система становится связанной с вводом-выводом, поскольку «следующий» UUID вряд ли будет кэширован.

Подумайте о переходе на AUTO_INCREMENT id. Это предпочтительнее для односерверных систем. Если вам нужно сгенерировать идентификаторы из нескольких местоположений, вам все равно могут понадобиться UUID.

Более для UUID.

Спасибо @Rick James. Я добавлю составные индексы и проверю производительность. приложение работает в нескольких местах. поэтому мы должны использовать UUID.

rsm 14.03.2019 14:26

Другие вопросы по теме