Mysql Query оптимизация, Переместить CASE Exists (SELECT ...) в левое соединение - PullRequest
2 голосов
/ 30 марта 2011

Обновление Вот ddl двух таблиц, которые использует оператор CASE

CREATE TABLE product_option (
merchant_id                 smallint unsigned NOT NULL,
product_option_id           smallint unsigned NOT NULL AUTO_INCREMENT,
product_option_name         varchar(255) NOT NULL DEFAULT '',
product_id                  int unsigned NOT NULL DEFAULT 0,    /* Option may be associated with a product  */
package_id                  int unsigned NOT NULL DEFAULT 0,    /* or all products with this package    */
PRIMARY KEY pk_product_option (merchant_id,product_option_id),

) TYPE=MyISAM PACK_KEYS=1 COMMENT='Options';

CREATE TABLE package_2_product (
merchant_id     smallint unsigned NOT NULL,
package_id      int unsigned NOT NULL,
product_id      int unsigned NOT NULL,

PRIMARY KEY pk_package_2_product (merchant_id,package_id,product_id)

) TYPE=MyISAM PACK_KEYS=1 COMMENT='Link product to package';

У меня есть запрос ...

SELECT SQL_CALC_FOUND_ROWS 
  p.*,
  CASE p.in_stock_msg  WHEN '' THEN 'In stock' ELSE p.in_stock_msg  END AS in_stock_msg, 
  CASE p.out_stock_msg WHEN '' THEN ''         ELSE p.out_stock_msg END AS out_stock_msg,
  CASE WHEN EXISTS (
    SELECT product_option_id 
      FROM product_option
     WHERE merchant_id = 116 AND product_id = p.product_id
     UNION
    SELECT product_id FROM package_2_product
     WHERE merchant_id = 116 AND product_id = p.product_id
  ) THEN 1 ELSE 0 END AS options_exist,
  i.thumbnail,i.thumbnail_width,i.thumbnail_height,
  i.title AS thumbnail_title, i.alt AS thumbnail_alt
FROM 
  product p 
  INNER JOIN category_2_product c2p ON p.merchant_id=c2p.merchant_id 
                                       AND p.product_id=c2p.product_id 
                                       AND c2p.category_id = 84
  LEFT  JOIN product_image        i ON p.merchant_id = i.merchant_id
                                       AND p.product_id = i.product_id
                                       AND i.is_default = 1 
WHERE 
  p.merchant_id = 116
  AND FIND_IN_SET('live',p.param) > 0
  AND FIND_IN_SET('wholesale-only',p.param) = 0
ORDER BY
  p.rank, p.product_name
LIMIT 0, 50;

Соответствующийчасть, являющаяся ДЕЙСТВИЕМ, КОГДА СУЩЕСТВУЕТ ... заставляет запрос выполняться несколько секунд.Без дополнительного выбора он завершается за одну десятую секунды.

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

Попытка выполнения нижеприведенного запроса с твиком к строке Coalesce и LEFT JOIN (SELECT ...) p1

Я получилэта ошибка ...

Код ошибки: 1054 Неизвестный столбец 'p1.product_id' in 'в предложении'

Вот запрос ...

SELECT SQL_CALC_FOUND_ROWS 
  p.*,
  CASE p.in_stock_msg  WHEN '' THEN 'In stock' ELSE p.in_stock_msg  END AS in_stock_msg,  
  CASE p.out_stock_msg WHEN '' THEN ''         ELSE p.out_stock_msg END AS out_stock_msg,
  COALESCE(p1.product_option_id, p2.product_id) IS NOT NULL AS options_exist,
  i.thumbnail,i.thumbnail_width,i.thumbnail_height,
  i.title AS thumbnail_title, i.alt AS thumbnail_alt
FROM 
  product p 
  INNER JOIN category_2_product c2p ON p.merchant_id=c2p.merchant_id 
                                       AND p.product_id=c2p.product_id 
                                       AND c2p.category_id = 84
  LEFT  JOIN product_image        i ON p.merchant_id = i.merchant_id
                                       AND p.product_id = i.product_id
                                       AND i.is_default = 1 
  LEFT JOIN (
   SELECT product_option_id
     FROM product_option
    WHERE merchant_id = 116
    GROUP BY product_id) p1 on p1.product_id = p.product_id
   LEFT JOIN (
    SELECT product_id
    FROM package_2_product
   WHERE merchant_id = 116
    GROUP BY product_id) p2 on p1.product_id is null and p2.product_id = p.product_id 
WHERE 
  p.merchant_id = 116
  AND FIND_IN_SET('live',p.param) > 0
  AND FIND_IN_SET('wholesale-only',p.param) = 0
ORDER BY
  p.rank, p.product_name

Ответы [ 2 ]

2 голосов
/ 30 марта 2011
SELECT SQL_CALC_FOUND_ROWS 
  p.*,
  CASE p.in_stock_msg  WHEN '' THEN 'In stock' ELSE p.in_stock_msg  END AS in_stock_msg, 
  CASE p.out_stock_msg WHEN '' THEN ''         ELSE p.out_stock_msg END AS out_stock_msg,
  COALESCE(p1.product_id, p2.product_id) IS NOT NULL AS options_exist,
  i.thumbnail,i.thumbnail_width,i.thumbnail_height,
  i.title AS thumbnail_title, i.alt AS thumbnail_alt
FROM 
  product p 
  INNER JOIN category_2_product c2p ON p.merchant_id=c2p.merchant_id 
                                       AND p.product_id=c2p.product_id 
                                       AND c2p.category_id = 84
  LEFT  JOIN product_image        i ON p.merchant_id = i.merchant_id
                                       AND p.product_id = i.product_id
                                       AND i.is_default = 1 
  LEFT JOIN (
    SELECT product_id
      FROM product_option
     WHERE merchant_id = 116
     GROUP BY product_id) p1 on p1.product_id = p.product_id
  LEFT JOIN (
    SELECT product_id
      FROM package_2_product
     WHERE merchant_id = 116
     GROUP BY product_id) p2 on p1.product_id is null and p2.product_id = p.product_id

WHERE 
  p.merchant_id = 116
  AND FIND_IN_SET('live',p.param) > 0
  AND FIND_IN_SET('wholesale-only',p.param) = 0
ORDER BY
  p.rank, p.product_name

Примечания:

  • on p1.product_id is null добавлено ко 2-му левому соединению, чтобы предотвратить его выполнение, когда p1 уже имеет результат.
  • Предложения group by не позволяют левым соединениям расширять набор результатов с помощью декартового произведения


Этот DDL создаст таблицу с достаточным количеством полей, чтобы показать, что запрос работает без ошибок.
create table product_option(merchant_id int, product_id int);
create table package_2_product(merchant_id int, product_id int);
create table category_2_product(merchant_id int, product_id int, category_id int);
create table product_image(merchant_id int, product_id int, is_default int,
    thumbnail int, thumbnail_width int, thumbnail_height int, title int, alt int);
create table product(merchant_id int, product_id int, in_stock_msg int,
    out_stock_msg int, param int, rank int, product_name int);
0 голосов
/ 30 марта 2011

Попробуйте это:

SELECT SQL_CALC_FOUND_ROWS p.*, 
                           CASE p.in_stock_msg 
                             WHEN '' THEN 'In stock' 
                             ELSE p.in_stock_msg 
                           END     AS in_stock_msg, 
                           CASE p.out_stock_msg 
                             WHEN '' THEN '' 
                             ELSE p.out_stock_msg 
                           END     AS out_stock_msg, 
                           CASE 
                             WHEN Isnull(po.product_option_id) 
                                  AND Isnull(p2p.product_id) THEN 0 
                             ELSE 1 
                           END     AS options_exist, 
                           i.thumbnail, 
                           i.thumbnail_width, 
                           i.thumbnail_height, 
                           i.title AS thumbnail_title, 
                           i.alt   AS thumbnail_alt 
FROM   product p 
       INNER JOIN category_2_product c2p 
         ON p.merchant_id = c2p.merchant_id 
            AND p.product_id = c2p.product_id 
            AND c2p.category_id = 84 
       LEFT JOIN product_image i 
         ON p.merchant_id = i.merchant_id 
            AND p.product_id = i.product_id 
            AND i.is_default = 1 
       LEFT JOIN product_option po 
         ON po.merchant_id = 116 
            AND po.product_id = p.product_id 
       LEFT JOIN package_2_product p2p 
         ON p2p.merchant_id = 116 
            AND p2p.product_id = p.product_id 
WHERE  p.merchant_id = 116 
       AND Find_in_set('live', p.param) > 0 
       AND Find_in_set('wholesale-only', p.param) = 0 
GROUP  BY p.prduct_id 
ORDER  BY p.rank, 
          p.product_name 
LIMIT  0, 50; 

вам понадобится индекс для обеих таблиц для

  • (merchant_id, product_id)
...