Помогите, пожалуйста, оптимизировать длительный запрос (левое внешнее объединение с двумя производными таблицами) - PullRequest
0 голосов
/ 31 декабря 2010

Мне нужен справочный запрос:

SELECT d.bn, d.4700, d.4500, ... , p.`Activity Description`  
FROM   
( SELECT temp.bn, temp.4700, temp.4500, ....  
FROM `tdata` temp  
GROUP BY temp.bn  
HAVING (COUNT(temp.bn) = 1) ) d   
LEFT OUTER JOIN  
( SELECT temp2.bn, max(temp2.FPE) AS max_fpe, temp2.`Activity Description`
FROM `pdata` temp2
GROUP BY temp2.bn ) p  
ON p.bn = d.bn;

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

Проблема связана сВторая производная таблица - она ​​не использует созданный мной индекс, и я не уверен, почему, похоже, из-за способа обработки полей TEXT.Первый подзапрос использует созданный мной индекс и работает довольно быстро, однако EXPLAIN для второго показывает: «Использование временного;Использование файловой сортировки.Пожалуйста, посмотрите индексы, которые я создал в приведенной ниже таблице.Может ли кто-нибудь помочь мне оптимизировать это?

В порядке быстрого объяснения первый подзапрос предназначен только для выбора записей, которые имеют уникальные значения bn, второй, в то время как выглядит немного странно (с функцией max, которая неиспользуется в наборе результатов), чтобы убедиться, что в набор результатов включена только одна запись из правой части объединения.

Мои операторы создания таблицы:

CREATE TABLE `tdata` (
`BN` varchar(15) DEFAULT NULL,
`4000` varchar(3) DEFAULT NULL,
`5800` varchar(3) DEFAULT NULL,
....
KEY `BN` (`BN`),
KEY `idx_t3010`(`BN`,`4700`,`4500`,`4510`,`4520`,`4530`,`4570`,`4950`,`5000`,`5010`,`5020`,`5050`,`5060`,`5070`,`5100`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

CREATE TABLE `pdata` (
`BN` varchar(15) DEFAULT NULL,
`FPE` datetime DEFAULT NULL,
`Activity Description` text,
....
KEY `BN` (`BN`),
KEY `idx_programs_2009` (`BN`,`FPE`,`Activity Description`(100))
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Спасибо!

РЕДАКТИРОВАТЬ

Спасибо Мальволио и Брайану Хуперу за их комментарии.Предложение Мальволио не сработает для меня, так как в обеих таблицах есть записи с одинаковыми значениями bn и нет общего поля, уникального для этих записей.

Это действительно сводится ко второму запросу производной таблицы:

SELECT temp2.bn, max(temp2.FPE) AS max_fpe, temp2.Activity Description 
FROM pdata temp2 
GROUP BY temp2.bn;

Независимо от того, что я здесь делаю, чтобы создать индекс в поле ТЕКСТ Описание деятельности, запрос не будет его использоватьсогласно ОБЪЯСНЕНИЮ.Если бы он использовал индекс, я уверен, что этот запрос будет работать отлично (так как первый запрос к производной таблице выполняется очень быстро).В качестве альтернативы, если бы существовал лучший способ структурирования этого запроса, чтобы обеспечить наличие только одной записи на bn, это тоже сработало бы.

Спасибо.

1 Ответ

1 голос
/ 31 декабря 2010

Подвыборы обычно являются самым быстрым способом медленного запроса. Я не уверен, что именно вы пытаетесь сделать, но вы можете выбрать BN из pdata с FPE с помощью следующего запроса

SELECT p.* FROM pdata p 
LEFT JOIN pdata p0 ON p.BN = p0.BN AND p.FPE < p0.FPE 
WHERE p0.BN IS NULL

Аналогично, если у вас был какой-то столбец в tdata, который был бы уникальным (или уникальным среди строк с одинаковым BN)

SELECT t.* FROM tdata t 
LEFT JOIN tdata t0 ON t.BN = t0.BN AND t.SOMEUNIQUEKEY != t0.SOMEUNIQUEKEY 
WHERE t0.BN IS NULL

Что-то странное в подселектах: они всегда намного медленнее, чем эквивалентное соединение. Я думаю, что это ошибка.

EDIT

Хупер, ихтиолог, не знал, как работает материал LEFT JOIN pdata p0 ON p.BN = p0.BN ... WHERE p0.BN IS NULL. Позвольте мне пройти это шаг за шагом с гораздо более простым примером. У вас есть имена таблиц с фамилией и именем, и вы хотите найти уникальные фамилии (то есть каждую фамилию, принадлежащую только одному человеку. Данные следующие:

last first
Smith Will
Smith John
Smith Adam
Jones John

Сначала попробуйте выполнить левое соединение самостоятельно

SELECT n1.last, n1.first, n2.last, n2.first FROM names n1 
  LEFT JOIN names n2 ON n1.last = n2.last and n1.first != n2.first

, который вернет

last first last first
Smith Will Smith John 
Smith John Smith Will
Smith Adam Smith John 
Smith Will Smith Adam 
Smith John Smith Adam 
Smith Adam Smith Will
Jones John NULL  NULL

Заметили эти нули в последнем ряду? Это не было несчастным случаем, это различие между обычным внутренним соединением и левым соединением. Внутреннее объединение (найти все пары строк с одинаковой фамилией и другим именем) нашло бы первые шесть, но проигнорировало бы непарный седьмой. Единственная функция LEFT JOIN - заполнять нулями все, что не заполнено предложением ON.

Теперь мы извлекаем только этот ряд:

SELECT n1.last, n1.first, n2.last, n2.first FROM names n1 
  LEFT JOIN names n2 ON n1.last = n2.last and n1.first != n2.first
  WHERE n2.last IS NULL

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

Та, и если можно, да.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...