MySQL - ВЫБЕРИТЕ, ГДЕ поле IN (подзапрос) - Чрезвычайно медленно почему? - PullRequest
119 голосов
/ 26 мая 2011

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

SELECT relevant_field
FROM some_table
GROUP BY relevant_field
HAVING COUNT(*) > 1

Таким образом, я получу все строки с релевантным полем, встречающиеся более одного раза. Этот запрос выполняется миллисекундами.

Теперь я хотел проверить каждый из дубликатов, поэтому я подумал, что могу ВЫБРАТЬ каждую строку в some_table с релевантным полем в приведенном выше запросе, поэтому я сделал так:

SELECT *
FROM some_table 
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)

По какой-то причине это происходит очень медленно (это занимает минуты). Что именно здесь происходит, чтобы сделать это так медленно? релевантное поле индексируется.

В конце концов я попытался создать представление "temp_view" из первого запроса (SELECT relevant_field FROM some_table GROUP BY relevant_field HAVING COUNT(*) > 1), а затем вместо этого создал свой второй запрос:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM temp_view
)

И это прекрасно работает. MySQL делает это за несколько миллисекунд.

Здесь есть эксперты по SQL, которые могут объяснить, что происходит?

Ответы [ 10 ]

109 голосов
/ 26 мая 2011

Перепишите запрос в этот

SELECT st1.*, st2.relevant_field FROM sometable st1
INNER JOIN sometable st2 ON (st1.relevant_field = st2.relevant_field)
GROUP BY st1.id  /* list a unique sometable field here*/
HAVING COUNT(*) > 1

Я думаю, st2.relevant_field должно быть в select, потому что в противном случае предложение having выдаст ошибку, но я не уверен на 100%

Никогда не используйте IN с подзапросом; как известно, это медленно.
Используйте IN только с фиксированным списком значений.

Дополнительные советы

  1. Если вы хотите делать запросы быстрее, не делать SELECT * только выбор поля, которые вам действительно нужны.
  2. Убедитесь, что у вас есть индекс relevant_field для ускорения равного соединения.
  3. Обязательно наберите group by на первичном ключе.
  4. Если вы используете InnoDB и , вы выбираете только индексированные поля (и все не слишком сложно) , чем MySQL разрешит ваш запрос, используя только индексы, что ускорит процесс.

Общее решение для 90% ваших IN (select запросов

Используйте этот код

SELECT * FROM sometable a WHERE EXISTS (
  SELECT 1 FROM sometable b
  WHERE a.relevant_field = b.relevant_field
  GROUP BY b.relevant_field
  HAVING count(*) > 1) 
99 голосов
/ 28 мая 2011

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

SELECT * FROM
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
) AS subquery

Окончательный запрос будет выглядеть так:

4 голосов
/ 26 мая 2011
SELECT st1.*
FROM some_table st1
inner join 
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)st2 on st2.relevant_field = st1.relevant_field;

Я попробовал ваш запрос в одной из моих баз данных, а также попытался переписать его как объединение с подзапросом.

Это сработало намного быстрее, попробуйте!

3 голосов
/ 04 апреля 2013

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

SELECT t1.*
FROM 
 some_table t1,
  (SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT (*) > 1) t2
WHERE
 t1.relevant_field = t2.relevant_field;
3 голосов
/ 26 мая 2011

Я переформатировал ваш медленный SQL-запрос с помощью www.prettysql.net

SELECT *
FROM some_table
WHERE
 relevant_field in
 (
  SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT ( * ) > 1
 );

При использовании таблицы как в запросе, так и в подзапросе вы всегда должны использовать оба псевдонима, например:

SELECT *
FROM some_table as t1
WHERE
 t1.relevant_field in
 (
  SELECT t2.relevant_field
  FROM some_table as t2
  GROUP BY t2.relevant_field
  HAVING COUNT ( t2.relevant_field ) > 1
 );

Это помогает?

1 голос
/ 21 августа 2017

Во-первых, вы можете найти повторяющиеся строки и найти количество строк, которое используется, сколько раз, и упорядочить их по числу, как это;

SELECT q.id,q.name,q.password,q.NID,(select count(*) from UserInfo k where k.NID= q.NID) as Count,
(
		CASE q.NID
		WHEN @curCode THEN
			@curRow := @curRow + 1
		ELSE
			@curRow := 1
		AND @curCode := q.NID
		END
	) AS No
FROM UserInfo q,
(
		SELECT
			@curRow := 1,
			@curCode := ''
	) rt
WHERE q.NID IN
(
    SELECT NID
    FROM UserInfo
    GROUP BY NID
    HAVING COUNT(*) > 1
) 

после этого создайте таблицу и вставьте в нее результат.

create table CopyTable 
SELECT q.id,q.name,q.password,q.NID,(select count(*) from UserInfo k where k.NID= q.NID) as Count,
(
		CASE q.NID
		WHEN @curCode THEN
			@curRow := @curRow + 1
		ELSE
			@curRow := 1
		AND @curCode := q.NID
		END
	) AS No
FROM UserInfo q,
(
		SELECT
			@curRow := 1,
			@curCode := ''
	) rt
WHERE q.NID IN
(
    SELECT NID
    FROM UserInfo
    GROUP BY NID
    HAVING COUNT(*) > 1
) 

Наконец, удалите дублирующие строки. Нет начала 0. За исключением первого номера каждой группы, удалите все дублирующие строки.

delete from  CopyTable where No!= 0;
1 голос
/ 10 марта 2014

иногда, когда данные растут больше, mysql WHERE IN может быть довольно медленным из-за оптимизации запросов.Попробуйте использовать STRAIGHT_JOIN, чтобы сообщить mysql о выполнении запроса как есть, например,

SELECT STRAIGHT_JOIN table.field FROM table WHERE table.id IN (...)

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

0 голосов
/ 30 ноября 2015

Я считаю, что это наиболее эффективно для обнаружения, если значение существует, логику можно легко инвертировать, чтобы найти, если значение не существует (то есть IS NULL);

SELECT * FROM primary_table st1
LEFT JOIN comparision_table st2 ON (st1.relevant_field = st2.relevant_field)
WHERE st2.primaryKey IS NOT NULL

* Замените релевантное поле именем значения, которое вы хотите проверить, существует в вашей таблице

* Замените primaryKey именем столбца первичного ключа в таблице сравнения.

0 голосов
/ 25 сентября 2014

Это похоже на мой случай, когда у меня есть таблица с именем tabel_buku_besar. Что мне нужно, это

  1. Ищем записи, которые имеют account_code='101.100' в tabel_buku_besar, которые имеют companyarea='20000', а также имеют IDR как currency

  2. Мне нужно получить все записи от tabel_buku_besar, у которых есть account_code, такой же, как в шаге 1, но есть transaction_number в шаге 1, результат

при использовании select ... from...where....transaction_number in (select transaction_number from ....) мой запрос выполняется очень медленно и иногда приводит к истечению времени ожидания запроса или делает мое приложение не отвечающим ...

Я пробую эту комбинацию и результат ... неплохо ...

`select DATE_FORMAT(L.TANGGAL_INPUT,'%d-%m-%y') AS TANGGAL,
      L.TRANSACTION_NUMBER AS VOUCHER,
      L.ACCOUNT_CODE,
      C.DESCRIPTION,
      L.DEBET,
      L.KREDIT 
 from (select * from tabel_buku_besar A
                where A.COMPANYAREA='$COMPANYAREA'
                      AND A.CURRENCY='$Currency'
                      AND A.ACCOUNT_CODE!='$ACCOUNT'
                      AND (A.TANGGAL_INPUT BETWEEN STR_TO_DATE('$StartDate','%d/%m/%Y') AND STR_TO_DATE('$EndDate','%d/%m/%Y'))) L 
INNER JOIN (select * from tabel_buku_besar A
                     where A.COMPANYAREA='$COMPANYAREA'
                           AND A.CURRENCY='$Currency'
                           AND A.ACCOUNT_CODE='$ACCOUNT'
                           AND (A.TANGGAL_INPUT BETWEEN STR_TO_DATE('$StartDate','%d/%m/%Y') AND STR_TO_DATE('$EndDate','%d/%m/%Y'))) R ON R.TRANSACTION_NUMBER=L.TRANSACTION_NUMBER AND R.COMPANYAREA=L.COMPANYAREA 
LEFT OUTER JOIN master_account C ON C.ACCOUNT_CODE=L.ACCOUNT_CODE AND C.COMPANYAREA=L.COMPANYAREA 
ORDER BY L.TANGGAL_INPUT,L.TRANSACTION_NUMBER`
...