Как я могу сделать этот запрос MySQL (с подзапросом) быстрее? - PullRequest
0 голосов
/ 15 января 2019

У меня следующий запрос, который получает данные заказа, работает нормально, но занимает 13,63 секунды :

SELECT
( SELECT meta_value FROM `myprefix_postmeta` as postmeta WHERE postmeta.meta_key = '_sku' AND postmeta.post_id = woocommerce_order_itemmeta3.meta_value ) AS sku,
woocommerce_order_items.order_item_name AS title,
SUM( woocommerce_order_itemmeta.meta_value ) AS quantity,
SUM( woocommerce_order_itemmeta2.meta_value ) AS total
FROM `myprefix_woocommerce_order_items` AS woocommerce_order_items
LEFT JOIN `myprefix_posts` AS posts ON posts.ID = woocommerce_order_items.order_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta ON woocommerce_order_itemmeta.order_item_id = woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta2 ON woocommerce_order_itemmeta2.order_item_id =   woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta3 ON woocommerce_order_itemmeta3.order_item_id =   woocommerce_order_items.order_item_id
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN( 'wc-completed', 'wc-processing', 'wc-on-hold' )
AND woocommerce_order_itemmeta.meta_key = '_qty'
AND woocommerce_order_itemmeta2.meta_key = '_line_total' 
AND woocommerce_order_itemmeta3.meta_key = '_product_id'
GROUP BY woocommerce_order_itemmeta3.meta_value
ORDER BY total DESC

Я хочу оптимизировать этот запрос, чтобы он выполнялся быстрее .

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

SELECT
postmeta.meta_value AS sku,
woocommerce_order_items.order_item_name AS title,
SUM( woocommerce_order_itemmeta.meta_value ) AS quantity,
SUM( woocommerce_order_itemmeta2.meta_value ) AS total
FROM `myprefix_woocommerce_order_items` AS woocommerce_order_items
LEFT JOIN `myprefix_posts` AS posts ON posts.ID = woocommerce_order_items.order_id
LEFT JOIN `myprefix_postmeta` AS postmeta ON posts.ID = woocommerce_order_items.order_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta ON woocommerce_order_itemmeta.order_item_id = woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta2 ON woocommerce_order_itemmeta2.order_item_id =   woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta3 ON woocommerce_order_itemmeta3.order_item_id =   woocommerce_order_items.order_item_id
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN( 'wc-completed', 'wc-processing', 'wc-on-hold' )
AND postmeta.meta_key = '_sku'
AND postmeta.post_id = woocommerce_order_itemmeta3.meta_value
AND woocommerce_order_itemmeta.meta_key = '_qty'
AND woocommerce_order_itemmeta2.meta_key = '_line_total' 
AND woocommerce_order_itemmeta3.meta_key = '_product_id'
GROUP BY woocommerce_order_itemmeta3.meta_value
ORDER BY total DESC

Это незначительно быстрее на миллисекунды , но есть ли способ оптимизировать это дальше?

Я ценю, что вам может понадобиться понять данные этих таблиц, они являются стандартными Таблицы базы данных WordPress & Схема базы данных WooCommerce , я не могу на самом деле поделиться даннымиза ними.

Есть вторая проблема со вторым примером выше, в том, что если SKU равен NULL, он не включает в себя строку, верхний включает его, так что в настоящее время это предпочтительный запрос..

Обновление # 1

Исходя из ответа: https://stackoverflow.com/a/54202562/8369600

Первоначально я обновил свой код в формате из части 1 ответаи это прекрасно работает.

Затем я использовал код в шаге 2 и внес некоторые незначительные изменения в форматирование для работы в phpMyAdmin для меня, эти изменения были:

SELECT
    (SELECT meta_value
        FROM `myprefix_postmeta` as postmeta 
        WHERE 
            postmeta.meta_key = '_sku' 
            AND postmeta.post_id = ( -- Substitute meta_value with CASE-expressions 
                    CASE
                        WHEN IM.meta_key = '_product_id' THEN IM.meta_value
                        ELSE NULL
                    END 
                ) -- IM3.meta_value               
            ) AS sku,
            -- think above is okay ^^^^
    I.order_item_name AS title,
    SUM(
        CASE -- Substitute meta_value with CASE-expressions 
            WHEN IM.meta_key = '_qty' THEN IM.meta_value
            ELSE 0 
        END 
    ) AS quantity,
    -- IM2.meta_value ) AS total
SUM( -- Substitute meta_value with CASE-expressions 
    CASE
            WHEN IM.meta_key = '_line_total' THEN IM.meta_value
            ELSE 0
    END 
) AS total       

FROM 
    `myprefix_woocommerce_order_items` AS I
    LEFT JOIN `myprefix_posts` AS posts 
    ON posts.ID = I.order_id
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM -- Make readable aliases
        ON IM.order_item_id = I.order_item_id 
            AND IM.meta_key IN('_qty', '_line_total', '_product_id')
    --         AND IM.meta_key = '_qty'   -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM2 
    --     ON IM2.order_item_id =   I.order_item_id
    --         AND IM2.meta_key = '_line_total' -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM3 
    --     ON IM3.order_item_id =   I.order_item_id
    --         AND IM3.meta_key = '_product_id' -- Move predicates from WHERE
WHERE 
    posts.post_type = 'shop_order'
    AND posts.post_status IN
    ( 'wc-completed', 'wc-processing', 'wc-on-hold' )
GROUP BY 
    (
        CASE
            WHEN IM.meta_key = '_product_id' THEN IM.meta_value
            ELSE NULL
        END 
    ) -- IM3.meta_value    
ORDER BY 
    total DESC

Однако это возвращает:

enter image description here

До того, как все строки будут заполнены данными, подозревайте, что случаи не работают в соответствии с оригиналом.

Примечание У меня естьтакже добавлена ​​ссылка на документацию базы данных WooCommerce, как было запрошено в одном из предыдущих комментариев.

Ответы [ 3 ]

0 голосов
/ 15 января 2019

Шаг 1. Создайте читаемые псевдонимы и переместите meta_key -предикаты в JOIN s

Я позволил себе отформатировать ваш запрос для ясности.

Я переименовал псевдонимы и переместил meta_key -предикаты из WHERE в JOIN s:

SELECT
    (SELECT meta_value
        FROM `myprefix_postmeta` as postmeta 
        WHERE 
            postmeta.meta_key = '_sku' 
            AND postmeta.post_id = IM3.meta_value ) AS sku,
    I.order_item_name AS title,
    SUM (IM.meta_value ) AS quantity,
    SUM (IM2.meta_value ) AS total
FROM 
    `myprefix_woocommerce_order_items` AS I
    LEFT JOIN `myprefix_posts` AS posts 
    ON posts.ID = I.order_id
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM -- Make readable aliases
        ON IM.order_item_id = I.order_item_id 
            AND IM.meta_key = '_qty'   -- Move predicates from WHERE
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM2 
        ON IM2.order_item_id =   I.order_item_id
            AND IM2.meta_key = '_line_total' -- Move predicates from WHERE
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM3 
        ON IM3.order_item_id =   I.order_item_id
            AND IM3.meta_key = '_product_id' -- Move predicates from WHERE
WHERE 
    posts.post_type = 'shop_order'
    AND posts.post_status IN
    ( 'wc-completed', 'wc-processing', 'wc-on-hold' )
GROUP BY 
    IM3.meta_value
ORDER BY 
    total DESC

Теперь у нас есть абсолютно эквивалентный запрос.

Давайте посмотрим, что мы можем сделать дальше.

  1. 3 LEFT JOIN с myprefix_woocommerce_order_itemmeta с тем же предикатом IM.order_item_id = I.order_item_id выглядит ненужным дублированием
  2. Но у нас есть разные предикаты meta_key

Шаг 2: заменить 3 JOIN только одним, заменить JOINed-поля CASE-выражениями

Итак, мы можем:

а. Замените 3 СОЕДИНЕНИЯ только одним СОЕДИНЕНИЕМ

б. Заменить поля IM / IM2 / IM3 CASE-выражениями

SELECT
    (SELECT meta_value
        FROM `myprefix_postmeta` as postmeta 
        WHERE 
            postmeta.meta_key = '_sku' 
            AND postmeta.post_id = ( -- Substitute meta_value with CASE-expressions 
                    CASE
                        WHEN IM.meta_key = '_product_id' THEN IM.meta_value
                        ELSE NULL
                    END 
                ) -- IM3.meta_value               
            ) AS sku,
    I.order_item_name AS title,
    SUM (
        CASE -- Substitute meta_value with CASE-expressions 
            WHEN IM.meta_key = '_qty' THEN IM.meta_value
            ELSE 0 
        END 
    ) AS quantity
    -- IM2.meta_value ) AS total
SUM ( -- Substitute meta_value with CASE-expressions 
    CASE
            WHEN IM.meta_key = '_line_total' THEN IM.meta_value
            ELSE 0
    END 
) AS total       

FROM 
    `myprefix_woocommerce_order_items` AS I
    LEFT JOIN `myprefix_posts` AS posts 
    ON posts.ID = I.order_id
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM -- Make readable aliases
        ON IM.order_item_id = I.order_item_id 
            AND IM.meta_key IN ('_qty', '_line_total', '_product_id')
    --         AND IM.meta_key = '_qty'   -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM2 
    --     ON IM2.order_item_id =   I.order_item_id
    --         AND IM2.meta_key = '_line_total' -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM3 
    --     ON IM3.order_item_id =   I.order_item_id
    --         AND IM3.meta_key = '_product_id' -- Move predicates from WHERE
WHERE 
    posts.post_type = 'shop_order'
    AND posts.post_status IN
    ( 'wc-completed', 'wc-processing', 'wc-on-hold' )
GROUP BY 
    (
        CASE
            WHEN IM.meta_key = '_product_id' THEN IM.meta_value
            ELSE NULL
        END 
    ) -- IM3.meta_value    
ORDER BY 
    total DESC

PS

Не очень удобно писать SQL без данных.

И, возможно, этот запрос некорректен для каждой версии mysql.

Так что постарайтесь понять принципы, а не просто скопировать и вставить мой код.

Сообщите нам о результатах или проблемах.

0 голосов
/ 15 января 2019

Если woocommerce является ответвлением WordPress, то они, вероятно, плохо справились с индексацией myprefix_woocommerce_order_itemmeta. Адаптируйте подсказки в здесь для улучшения производительности.

Я согласен с другими - шаблон схемы EAV - отстой. Мои предложения по индексам выше помогают некоторые .

Например, и у «order» всегда есть qty, product_id, line_total; так почему бы не использовать их как настоящие столбцы, если они скрыты в другой таблице, как если бы они были редкими атрибутами? EAV в некоторой степени необходим для торгового сайта, где только некоторые товары имеют «размер платья», «F-стоп» или «тип передачи». (В этом случае JSON может быть лучшим шаблоном, чем EAV.)

Схема обзора

Тьфу, woocommerce хуже чем WP ( ссылка ):

CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
  meta_id BIGINT UNSIGNED NOT NULL auto_increment,
  order_item_id BIGINT UNSIGNED NOT NULL,
  meta_key varchar(255) default NULL,
  meta_value longtext NULL,
  PRIMARY KEY  (meta_id),
  KEY order_item_id (order_item_id),
  KEY meta_key (meta_key(32))
) $collate;

Индексирование префиксов (meta_key(32)) редко бывает полезным и часто приводит к обратным результатам. Тем не менее, вероятно, все мои рекомендации (см. Ссылку выше) применимы, с подходящими изменениями имени.

0 голосов
/ 15 января 2019

Первое, что нужно сделать, это проверить, есть ли у вас правильные индексы для всех столбцов, где вы ЛЕВЫЕ СОЕДИНЯЕТСЯ и где вы фильтруете. Обычно отсутствие индексов является причиной медленных запросов.

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

Хотя это и не обязательно, вы также можете попробовать перенести ключи, такие как wc-complete, wc-processing, в их собственную таблицу и выполнить соединения с помощью INT.

Вы также можете попытаться нормализовать SUM или даже создать для них вычисляемый столбец.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...