如何优化通过NOT IN或NOT EXISTS

时间:2017-11-27 12:44:09

标签: php mysql

通过添加一个查询过滤器来查询我的性能低下。

以下是完整查询:

SELECT
    `c`.`categories_id`,
    `c`.`section_id`,
    `c`.`categories_status`,
    IF(`c`.`categories_status` = 1, 'ON', 'OFF') AS `categories_status_name`,
    TRIM(`cd`.`categories_name`) AS `categories_name`,
    IF(`cd`.`concert_date` <> '',
        DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%d.%m.%Y'),
        NULL
    ) AS `concert_date`,
    TRIM(`cd`.`concert_time`) AS `concert_time`
FROM
    `categories` `c`
    JOIN `categories_description` `cd` ON `c`.`categories_id` = `cd`.`categories_id` 
WHERE
    `c`.`plan_id` > 2
 AND 
    `c`.`categories_status` = '1' 
 AND 
    `cd`.`categories_id` NOT IN(
    SELECT
        `p`.`parent_id`
    FROM
        `products` `p`
    WHERE
        `p`.`product_type` = 'X'
    AND
        `p`.`parent_id` = `cd`.`categories_id`
    GROUP BY `p`.`product_type`
) 
GROUP BY `c`.`categories_id`
ORDER BY DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%Y-%m-%d') DESC, `cd`.`categories_name` DESC

在这个查询中,我有一个新的过滤器,添加的内容如下所示:

 `cd`.`categories_id` NOT IN(
        SELECT
            `p`.`parent_id`
        FROM
            `products` `p`
        WHERE
            `p`.`product_type` = 'X'
        AND
            `p`.`parent_id` = `cd`.`categories_id`
        GROUP BY `p`.`product_type`
    )

我还尝试使用NOT EXISTS再做一个解决方案,但这更糟糕了:

NOT EXISTS (
     SELECT
        DISTINCT 1
    FROM
        `products` `p`
    WHERE
        `p`.`product_type` = 'X'
    AND
        `p`.`parent_id` = `cd`.`categories_id`
    GROUP BY `p`.`product_type`
)   

我的主要问题是,在我添加此过滤器以删除包含X产品的类别后,性能开始变得非常糟糕。如果没有此过滤器页面,则加载时间为0.5-0.8秒,但此过滤器页面加载时间为8到10秒。

有人可以帮我优化此查询吗?

2 个答案:

答案 0 :(得分:1)

这可行。大多数引擎在NOT IN / NOT EXISTS中都不是很好,除非它们在内部将查询修改为下面的表单。 至少值得一试。

SELECT
    `c`.`categories_id`,
    `c`.`section_id`,
    `c`.`categories_status`,
    IF(`c`.`categories_status` = 1, 'ON', 'OFF') AS `categories_status_name`,
    TRIM(`cd`.`categories_name`) AS `categories_name`,
    IF(`cd`.`concert_date` <> '',
        DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%d.%m.%Y'),
        NULL
    ) AS `concert_date`,
    TRIM(`cd`.`concert_time`) AS `concert_time`
FROM
    `categories` `c` JOIN `categories_description` `cd` 
                     ON `c`.`categories_id` = `cd`.`categories_id`
     LEFT JOIN  `products` `p`
     ON `p`.`parent_id` = `cd`.`categories_id`
     AND `p`.`product_type` = 'X'
WHERE
    `c`.`plan_id` > 2
 AND 
    `c`.`categories_status` = '1' 
 AND 
    `p`.`parent_id` IS NULL 
GROUP BY `c`.`categories_id`
ORDER BY DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%Y-%m-%d') DESC, `cd`.`categories_name` DESC

答案 1 :(得分:0)

这是我得到的最快的解决方案。

SELECT
    `c`.`categories_id`,
    `c`.`section_id`,
    `c`.`categories_status`,
    IF(`c`.`categories_status` = 1, 'ON', 'OFF') AS `categories_status_name`,
    TRIM(`cd`.`categories_name`) AS `categories_name`,
    IF(`cd`.`concert_date` <> '',
        DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%d.%m.%Y'),
        NULL
    ) AS `concert_date`,
    TRIM(`cd`.`concert_time`) AS `concert_time`
FROM
    `categories` `c`
    INNER JOIN `categories_description` `cd` ON `c`.`categories_id` = `cd`.`categories_id`
    LEFT JOIN `products` `p` ON `p`.`parent_id` = `cd`.`categories_id`
WHERE
    `c`.`plan_id` > 2
 AND 
    `c`.`categories_status` = '1'
 AND `p`.`product_type` != 'X'
GROUP BY `c`.`categories_id`
ORDER BY DATE_FORMAT(STR_TO_DATE(`cd`.`concert_date`,'%d/%m/%Y'),'%Y-%m-%d') DESC, `cd`.`categories_name` DESC

感谢@NigelRen,他让我知道如何解决这个问题。 @Ronald给出了几乎相同的解决方案,但比我的解决方案慢了一点(0.400秒)。

谢谢你们的帮助!