Оптимизация запроса MySQL с большим предложением IN () или объединение на производной таблице - PullRequest
0 голосов
/ 19 января 2010

Допустим, мне нужно запросить сотрудников корпорации. У меня есть таблица «транзакции», которая содержит данные о каждой сделанной транзакции.

CREATE TABLE `transactions` (
  `transactionID` int(11) unsigned NOT NULL,
  `orderID` int(11) unsigned NOT NULL,
  `customerID` int(11) unsigned NOT NULL,
  `employeeID` int(11) unsigned NOT NULL, 
  `corporationID` int(11) unsigned NOT NULL,
  PRIMARY KEY (`transactionID`),
  KEY `orderID` (`orderID`),
  KEY `customerID` (`customerID`),
  KEY `employeeID` (`employeeID`),
  KEY `corporationID` (`corporationID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

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

Например, если бы сотрудники А и В из корпорации 1 оба были вовлечены в продажу пылесоса корпорации 2, в таблице «транзакции» было бы две записи; один для каждого сотрудника, и оба для корпорации 1. Однако это не должно влиять на результаты. Сделка от корпорации 1, независимо от того, сколько ее сотрудников было вовлечено, должна рассматриваться как одна.

Полегче, подумал я. Я просто сделаю соединение на производной таблице, вот так:

SELECT corporationID FROM transactions JOIN (SELECT DISTINCT orderID FROM transactions WHERE corporationID = 1) AS foo USING (orderID)

Запрос возвращает список корпораций, которые участвовали в сделках с корпорацией 1. Это именно то, что мне нужно, но он очень медленный, потому что MySQL не может использовать индекс corporationID для определения производной таблицы. Я понимаю, что это относится ко всем подзапросам / производным таблицам в MySQL.

Я также пытался запросить коллекцию orderID отдельно и использовать смехотворно большое предложение IN () (как правило, 100 000+ идентификаторов), но, как оказалось, MySQL имеет проблемы с использованием индексов для смехотворно больших предложений IN () в ну и в результате время запроса не улучшается.

Есть ли другие доступные варианты, или я исчерпал их обоих?

Ответы [ 2 ]

1 голос
/ 19 января 2010

Если я понимаю ваше требование, вы можете попробовать это.

select distinct t1.corporationID
from transactions t1
where exists (
    select 1
    from transactions t2
    where t2.corporationID =  1
    and t2.orderID = t1.orderID)
and t1.corporationID != 1;

или это:

select distinct t1.corporationID
from transactions t1
join transactions t2
on t2.orderID = t1.orderID
and t1.transactionID != t2.transactionID
where t2.corporationID = 1
and t1.corporationID != 1;
0 голосов
/ 19 января 2010

Ваши данные не имеют смысла для меня, я думаю, что вы используете corporationID, где в какой-то момент вы имеете в виду идентификатор клиента, поскольку ваш запрос соединяет таблицу транзакций с таблицей транзакций для corporationID = 1 на основе orderID для получения идентификаторов корпорации ... что тогда будет 1, верно?

Не могли бы вы указать, что означают идентификаторы customerID, employeeID и corporationID? Как я узнаю, что сотрудники A и B являются сотрудниками корпорации 1 - в этом случае корпорация 1 является идентификатором корпорации, а корпорация 2 является клиентом и хранится в идентификаторе клиента?

Если это так, вам просто нужно сделать группу по:

SELECT customerID
FROM transactions
WHERE corporationID = 1
GROUP BY customerID

(Или выберите и сгруппируйте по идентификатору заказа, если вы хотите одну строку для заказа вместо одной строки для клиента.)

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

И наоборот, возвращает все корпорации, которые продали корпорации 2.

SELECT corporationID
FROM transactions
WHERE customerID = 2
GROUP BY corporationID
...