Ivijan Stefan Stipić
Ivijan Stefan Stipić

Reputation: 6678

MySQL how to optimize query with bunch of subqueries

I have one huge project where I need to made statistics. This query give me accurate results but is a bit slow on loading and need somehow to optimize it but can't figure out how.

SELECT
    TRIM(`op`.`products_quantity`) AS `products_quantity`,
    TRIM(`op`.`products_price`) AS `products_price`,
    TRIM(`op`.`orders_products_status`) AS `orders_products_status`,
    TRIM(`p`.`product_type`) AS `product_type`,
    IF(`p`.`product_type` IN ('G'),FLOOR(`op`.`final_price`*`op`.`products_quantity`),
        `op`.`final_price`) AS `final_price`,
    TRIM((
        SELECT
            `o`.`payment_method`
        FROM
            `orders` `o`
        WHERE
            `o`.`orders_id` = `op`.`orders_id`
        GROUP BY `o`.`orders_id`
    )) AS `payment_method`,
    IF(
        TRIM(DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%d/%m/%Y')) LIKE (
            SELECT  
                TRIM(DATE_FORMAT(`o`.`date_purchased`,'%d/%m/%Y'))
            FROM
                `orders` `o`
            WHERE
                `o`.`orders_id` = `op`.`orders_id`
            GROUP BY `o`.`orders_id`
        ),
        1,0
    ) AS `same_day`
FROM
    `categories` `c`,
    `categories_description` `cd`,
    `products` `p`,
    `orders_products` `op`
WHERE
    `c`.`section_id` = 25
AND
    `cd`.`categories_id` = `c`.`categories_id`
AND
    `p`.`section_id` = `c`.`section_id`
AND
    `p`.`product_type` IN ('P')
AND
    `op`.`products_id` = `p`.`products_id`
GROUP BY `op`.`orders_products_id`
ORDER BY `payment_method` ASC

Do anyone have some suggestion?

I would like also some good explanation from SQL experts how is the best way to organize similar query and what need to be first inside WHERE section.

Upvotes: 2

Views: 84

Answers (3)

Keyur Panchal
Keyur Panchal

Reputation: 1402

As there is no data, I am not able to verify the query. But I think the following optional query may be helpful:

SELECT
    TRIM(`op`.`products_quantity`) AS `products_quantity`,
    TRIM(`op`.`products_price`) AS `products_price`,
    TRIM(`op`.`orders_products_status`) AS `orders_products_status`,
    TRIM(`p`.`product_type`) AS `product_type`,
    IF(`p`.`product_type` = 'G', FLOOR(`op`.`final_price` * `op`.`products_quantity`), `op`.`final_price`) AS `final_price`,
    TRIM(`t`.`payment_method`) AS `payment_method`,
    IF( TRIM(DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%d/%m/%Y')) LIKE 
        TRIM(DATE_FORMAT(`t`.`date_purchased`,'%d/%m/%Y')), 1, 0 ) AS `same_day`
FROM
    `categories` `c` INNER JOIN `categories_description` `cd` ON `c`.`categories_id` = `cd`.`categories_id`
    INNER JOIN `products` `p` ON `c`.`section_id` = `p`.`section_id`
    INNER JOIN `orders_products` `op` ON `p`.`products_id` = `op`.`products_id`
    INNER JOIN
    ( SELECT `o`.`orders_id`, `o`.`payment_method`, 
       TRIM(DATE_FORMAT(`o`.`date_purchased`,'%d/%m/%Y')) AS `date_purchased`
       FROM `orders` `o` GROUP BY `o`.`orders_id` ) AS `t` ON `op`.`orders_id` = `t`.`orders_id`
WHERE `c`.`section_id` = 25 AND `p`.`product_type` IN ('P')
GROUP BY `op`.`orders_products_id`
ORDER BY `payment_method`;

Please note that following portion was common for both Inner sub-queries:

FROM `orders` `o`
WHERE `o`.`orders_id` = `op`.`orders_id`
GROUP BY `o`.`orders_id`

Which means you are referring to the same table with same conditions twice, just to get two different columns. So I have removed that part and added it as INNER JOIN of single sub-query.

Also note that there is only one value in bracket for both IN. So I have replaced IN by =.

Please let me know if this is helpful to you.

Upvotes: 1

Bibhudatta Sahoo
Bibhudatta Sahoo

Reputation: 4904

You can do this more better way by Joining all tables by using JION

Try like this :-

SELECT
        TRIM(`op`.`products_quantity`) AS `products_quantity`,
        TRIM(`op`.`products_price`) AS `products_price`,
        TRIM(`op`.`orders_products_status`) AS `orders_products_status`,
        TRIM(`p`.`product_type`) AS `product_type`,
        IF(`p`.`product_type` IN ('G'),FLOOR(`op`.`final_price`*`op`.`products_quantity`),
            `op`.`final_price`) AS `final_price`,
        TRIM((
            SELECT
                `o`.`payment_method`
            FROM
                `orders` `o`
            WHERE
                `o`.`orders_id` = `op`.`orders_id`
            GROUP BY `o`.`orders_id`
        )) AS `payment_method`,
        IF(
            TRIM(DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%d/%m/%Y')) LIKE (
                SELECT  
                    TRIM(DATE_FORMAT(`o`.`date_purchased`,'%d/%m/%Y'))
                FROM
                    `orders` `o`
                WHERE
                    `o`.`orders_id` = `op`.`orders_id`
                GROUP BY `o`.`orders_id`
            ),
            1,0
        ) AS `same_day`
    FROM
        `categories` `c`
        join `categories_description` `cd` on `cd`.`categories_id` = `c`.`categories_id`
        join `products` `p` on `p`.`section_id` = `c`.`section_id`
        join `orders_products` `op` on `op`.`products_id` = `p`.`products_id`
    WHERE
        `c`.`section_id` = 25
    AND
        `p`.`product_type` IN ('P')

    GROUP BY `op`.`orders_products_id`
    ORDER BY `payment_method` ASC

Upvotes: 2

informer
informer

Reputation: 851

You can try and run few basic steps:

  • Check/Add Indexes on joining columns
  • Run smaller queries separately and improve them first. Use 'Explain' to check the number of rows used to return the results. Try to reduce this number.
  • Follow the same steps for the bigger query.

Upvotes: 1

Related Questions