it-roy-ru.com

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

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

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, которые могут объяснить, что происходит?

113
quano

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

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

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

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT * FROM
    (
        SELECT relevant_field
        FROM some_table
        GROUP BY relevant_field
        HAVING COUNT(*) > 1
    ) AS subquery
)
93
quano

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

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) 
107
Johan
5
edze
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;

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

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

4
ceteras

Попробуй это

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
user2244323

Я переформатировал ваш медленный 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
 );

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

3
plang

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

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

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

1
Andrey Posudevsky

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

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;

0
harun ugur

Это похоже на мой случай, когда у меня есть таблица с именем 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`
0
Hilarius L. Doren

Я считаю, что это наиболее эффективно для обнаружения, если значение существует, логику можно легко перевернуть, чтобы найти, если значение не существует (то есть 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
Matt