2009-10-12 3 views
1

У меня есть пара таблиц (продуктов и поставщиков) и вы хотите узнать, какие элементы больше не указаны в таблице поставщиков.Эффективный запрос MySQL для поиска записей в A, где не сопоставляется B

В таблице uc_products есть продукты. Таблица uc_supplier_csv имеет запасы поставщиков. uc_products.model присоединяется к uc_suppliers.sku.

Я вижу очень длинные запросы при попытке определить запас в таблице продуктов, которые не указаны в таблице поставщиков. Я хочу только извлечь nid записей, которые соответствуют; sid IS NULL - это только то, что я могу определить, у каких предметов нет поставщика.

Для первого из нижеприведенных запросов сервер БД (4 ГБ RAM/2x 2.4 ГГц) занимает час, чтобы получить результат (507 строк). Я не дождался завершения второго запроса.

Как я могу сделать этот запрос более оптимальным? Это связано с несоответствующими наборами символов?

Я думал, что следующее будет наиболее эффективная SQL использовать:

  SELECT nid, sid 
      FROM uc_products p 
LEFT OUTER JOIN uc_supplier_csv c 
      ON p.model = c.sku 
     WHERE sid IS NULL ; 

Для этого запроса я получаю следующий EXPLAIN результата:

mysql> EXPLAIN SELECT nid, sid FROM uc_products p LEFT OUTER JOIN uc_supplier_csv c ON p.model = c.sku WHERE sid IS NULL; 
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra     | 
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------------------+ 
| 1 | SIMPLE  | p  | ALL | NULL   | NULL | NULL | NULL | 6526 |       | 
| 1 | SIMPLE  | c  | ALL | NULL   | NULL | NULL | NULL | 126639 | Using where; Not exists | 
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------------------+ 
2 rows in set (0.00 sec) 

я бы подумал, что ключи idx_sku и idx_model будут действительны для использования здесь, но это не так. Это потому, что кодировки по умолчанию для таблиц не совпадают? Один из них - UTF-8, а один - латинский.

Я также рассмотрел эту форму:

SELECT nid 
    FROM uc_products 
WHERE model 
NOT IN ( 
     SELECT DISTINCT sku FROM uc_supplier_csv 
     ) ; 

EXPLAIN показывает следующие результаты для этого запроса:

mysql> explain select nid from uc_products where model not in (select sku from uc_supplier_csv) ; 
+----+--------------------+-----------------+-------+-----------------------+---------+---------+------+--------+--------------------------+ 
| id | select_type  | table   | type | possible_keys   | key  | key_len | ref | rows | Extra     | 
+----+--------------------+-----------------+-------+-----------------------+---------+---------+------+--------+--------------------------+ 
| 1 | PRIMARY   | uc_products  | ALL | NULL     | NULL | NULL | NULL | 6520 | Using where    | 
| 2 | DEPENDENT SUBQUERY | uc_supplier_csv | index | idx_sku,idx_sku_stock | idx_sku | 258  | NULL | 126639 | Using where; Using index | 
+----+--------------------+-----------------+-------+-----------------------+---------+---------+------+--------+--------------------------+ 
2 rows in set (0.00 sec) 

И только поэтому я не упустить ничего из, вот несколько более захватывающим детали: размеры и статистика стола и структура стола :)

mysql> show table status where Name in ('uc_supplier_csv', 'uc_products') ; 
+-----------------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-------------------+----------+----------------+---------+ 
| Name   | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time   | Update_time   | Check_time   | Collation   | Checksum | Create_options | Comment | 
+-----------------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-------------------+----------+----------------+---------+ 
| uc_products  | MyISAM |  10 | Dynamic | 6520 |    89 |  585796 | 281474976710655 |  232448 |  912 |   NULL | 2009-04-24 11:03:15 | 2009-10-12 14:23:43 | 2009-04-24 11:03:16 | utf8_general_ci |  NULL |    |   | 
| uc_supplier_csv | MyISAM |  10 | Dynamic | 126639 |    26 |  3399704 | 281474976710655 |  5864448 |   0 |   NULL | 2009-10-12 14:28:25 | 2009-10-12 14:28:25 | 2009-10-12 14:28:27 | latin1_swedish_ci |  NULL |    |   | 
+-----------------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-------------------+----------+----------------+---------+ 

и

CREATE TABLE `uc_products` (
    `vid` mediumint(9) NOT NULL default '0', 
    `nid` mediumint(9) NOT NULL default '0', 
    `model` varchar(255) NOT NULL default '', 
    `list_price` decimal(10,2) NOT NULL default '0.00', 
    `cost` decimal(10,2) NOT NULL default '0.00', 
    `sell_price` decimal(10,2) NOT NULL default '0.00', 
    `weight` float NOT NULL default '0', 
    `weight_units` varchar(255) NOT NULL default 'lb', 
    `length` float unsigned NOT NULL default '0', 
    `width` float unsigned NOT NULL default '0', 
    `height` float unsigned NOT NULL default '0', 
    `length_units` varchar(255) NOT NULL default 'in', 
    `pkg_qty` smallint(5) unsigned NOT NULL default '1', 
    `default_qty` smallint(5) unsigned NOT NULL default '1', 
    `unique_hash` varchar(32) NOT NULL, 
    `ordering` tinyint(2) NOT NULL default '0', 
    `shippable` tinyint(2) NOT NULL default '1', 
    PRIMARY KEY (`vid`), 
    KEY `idx_model` (`model`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 

CREATE TABLE `uc_supplier_csv` (
    `sid` int(10) unsigned NOT NULL default '0', 
    `sku` varchar(255) default NULL, 
    `stock` int(10) unsigned NOT NULL default '0', 
    `list_price` decimal(8,2) default '0.00', 
    KEY `idx_sku` (`sku`), 
    KEY `idx_stock` (`stock`), 
    KEY `idx_sku_stock` (`sku`,`stock`), 
    KEY `idx_sid` (`sid`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

EDIT: Добавление планов запросов для нескольких предложенных запросов от Мартина ниже:

mysql> explain SELECT nid FROM uc_products p WHERE NOT EXISTS (SELECT 1 FROM uc_supplier_csv c WHERE p.model = c.sku) ; 
+----+--------------------+-------+-------+---------------+---------+---------+------+--------+--------------------------+ 
| id | select_type  | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+--------------------+-------+-------+---------------+---------+---------+------+--------+--------------------------+ 
| 1 | PRIMARY   | p  | ALL | NULL   | NULL | NULL | NULL | 6526 | Using where    | 
| 2 | DEPENDENT SUBQUERY | c  | index | NULL   | idx_sku | 258  | NULL | 126639 | Using where; Using index | 
+----+--------------------+-------+-------+---------------+---------+---------+------+--------+--------------------------+ 
2 rows in set (0.00 sec) 

mysql> explain SELECT nid FROM uc_products WHERE model NOT IN (SELECT sku FROM uc_supplier_csv) ; 
+----+--------------------+-----------------+-------+-----------------------+---------+---------+------+--------+--------------------------+ 
| id | select_type  | table   | type | possible_keys   | key  | key_len | ref | rows | Extra     | 
+----+--------------------+-----------------+-------+-----------------------+---------+---------+------+--------+--------------------------+ 
| 1 | PRIMARY   | uc_products  | ALL | NULL     | NULL | NULL | NULL | 6526 | Using where    | 
| 2 | DEPENDENT SUBQUERY | uc_supplier_csv | index | idx_sku,idx_sku_stock | idx_sku | 258  | NULL | 126639 | Using where; Using index | 
+----+--------------------+-----------------+-------+-----------------------+---------+---------+------+--------+--------------------------+ 
2 rows in set (0.00 sec) 
+2

Использование HAVING в первом запросе неверно - так как нет GROUP BY, оно должно быть простым ГДЕ. Не уверен, почему MySQL не дает вам сообщение об ошибке, но я думаю, это то, что испортило план запроса! –

+0

спасибо alex - обновлен –

+0

Я проверил четыре формы запросов на этой странице вчера вечером на моем ноутбуке (MBP2.4GHz/4GB/OSX/MAMP MySQL). * Форма LEFT OUTER JOIN выше принята 3526s для выполнения. * В приведенной выше форме подзаголовка выполнено 1021 секунд. * Предложение Мартина ниже заняло 637 секунд. * Джеймс был немного быстрее, чем Мартин, но вернул другой результат из трех других форм. –

ответ

3

Возможно попробовать использовать NOT EXISTS, а не рассчитывает? Например:

SELECT nid 
    FROM uc_products p 
WHERE NOT EXISTS ( 
     SELECT 1 
     FROM uc_supplier_csv c 
     WHERE p.model = c.sku 
     ) 

SO пользователь Quassnoi имеет short article изложение некоторых тестов, которые предполагают, что это может быть стоит попробовать:

SELECT nid 
    FROM uc_products 
WHERE model NOT IN ( 
     SELECT sku 
     FROM uc_supplier_csv 
     ) 

в основном в соответствии с вашим исходным запросом, без исключения.

Еще один для вас Криса, на этот раз с помощью сшитого кодирования присоединиться:

SELECT nid 
    FROM uc_products p 
WHERE NOT EXISTS (
     SELECT 1 
     FROM uc_supplier_csv c 
     WHERE CONVERT(p.model USING latin1) = c.sku 
     ) 
+0

Этот запрос был самым быстрым предлагаемым решением для возврата правильного результата. Для выполнения потребовалось 637 секунд. –

+0

Как выглядел план запроса? –

+0

добавлен в вопрос (форматирование не работает для меня в комментариях?) –