MySQL-SELECT WHERE 필드 IN (하위 쿼리)-왜 느리게?


133

검사하려는 데이터베이스에 두 개의 중복 항목이 있으므로 중복 항목을 확인하기 위해 수행 한 작업은 다음과 같습니다.

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

이렇게하면 related_field가있는 모든 행을 두 번 이상 가져옵니다. 이 쿼리는 실행하는 데 밀리 초가 걸립니다.

이제 각 중복 항목을 검사하고 싶었으므로 위 쿼리에서 related_field를 사용하여 some_table의 각 행을 선택할 수 있다고 생각했습니다.

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

이것은 어떤 이유로 외부 적으로 느리게 나타납니다 (분이 걸립니다). 그것을 느리게 만들기 위해 여기서 정확히 무슨 일이 일어나고 있습니까? related_field가 색인됩니다.

결국 첫 번째 query에서 "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 전문가가 있습니까?


정확히 무엇을 원하십니까? 하나를 제외한 중복 항목을 삭제하고 싶습니까 ?? 제안 : 제발 읽기 자동 가입
diEcho

1
분명히 그룹별로 느린 것입니다 ...
ajreal

첫 번째 쿼리는 밀리 초 (HAVING을 사용한 그룹화 및 필터링)로 실행됩니다. 다른 쿼리와 함께 사용하면 모든 것이 느려집니다 (분이 걸립니다).
quano

@diEcho, 중복을 찾아서 검사하고 수동으로 삭제하고 싶습니다.
quano

답변:


112

이 질문을 다시 작성하십시오

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그렇지 않으면 having절에 오류가 발생 하기 때문에 선택에 있어야 한다고 생각 하지만 100 % 확신 할 수는 없습니다.

IN하위 쿼리와 함께 사용하지 마십시오 . 이것은 매우 느립니다. 고정 된 값 목록
만 사용하십시오 IN.

더 많은 팁

  1. 쿼리 속도를 높이려면 SELECT *실제로 필요한 필드 만 선택 하지 마십시오 .
  2. relevant_field동등 조인 속도를 높이려면 인덱스가 있어야합니다 .
  3. group by기본 키 를 확인하십시오 .
  4. 당신은 이노에있는 경우 만 인덱스 필드를 선택 (그리고 상황이 너무 복잡하지 않은) 의 MySQL은 인덱스를 사용하여 쿼리를 해결할 수보다 일의 길을 속도.

IN (select 쿼리의 90 %를위한 일반 솔루션

이 코드를 사용하십시오

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) 

1
로 쓸 수도 있습니다 HAVING COUNT(*) > 1. 일반적으로 MySQL에서는 더 빠릅니다.
ypercubeᵀᴹ

@ypercube, 하단 쿼리에 대해 수행하면 상단 쿼리에 대해 결과가 변경 될 것이라고 생각합니다.
Johan

@Johan : st2.relevant_fieldNULL(이 ON절에 이미 포함되어 있지 않으므로) 결과를 변경하지 않습니다.
ypercubeᵀᴹ

당신이 계산에 수를 (멀리) 변경할 수 있도록 @ypercube (*) 경우 당신이있어 확인 afield없을 것 null, 그것을 얻었다. 감사합니다
Johan

1
@quano, 예 는 on 이 아닌 on 이기 때문에 모든 중복 항목을 나열 합니다 . group byst1.idst1.relevant_field
Johan

110

하위 쿼리는 상관 된 쿼리이므로 각 행에 대해 실행됩니다. 하위 쿼리에서 다음과 같이 모든 항목을 선택하여 상관 쿼리를 상관되지 않은 쿼리로 만들 수 있습니다.

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
)

3
이것은 나를 위해 놀랍게 잘 작동했습니다. IN (하위 쿼리) 내에 또 다른 IN (하위 쿼리)이 있었고 10 분 이상이 걸렸기 때문에 기다리는 동안 Google을 검색했습니다. 제안한대로 SELECT * FROM ()으로 각 하위 쿼리를 래핑하면 2 초로 줄었습니다!
Liam

감사합니다. 지금 몇 시간 동안이 작업을 수행하는 좋은 방법을 찾으려고 노력했습니다. 이것은 완벽하게 작동했습니다. 난 당신에게 더 많은 투표를 줄 수 있으면 좋겠다! 이것은 분명히 답이되어야합니다.
thaspius

완벽하게 작동합니다. 실행하는 데 ~ 50 초가 걸리는 쿼리가 이제 순식간에 이루어집니다. 더 많은 투표를 할 수 있기를 바랍니다. 때로는 조인을 사용할 수 없으므로 이것이 정답입니다.
simon

옵티마이 저가 노조와 관련된 쿼리를 고려하는 이유가 궁금합니다 ... 어쨌든,이 트릭은 마술처럼 작동했습니다
Brian Leishman 1

2
상관 하위 쿼리를 만드는 이유를 설명해 주시겠습니까? 외부 쿼리에 의존하는 값을 사용할 때 하위 쿼리가 상관 관계가 있다는 것을 이해합니다. 그러나이 예에서는 상호 의존성을 볼 수 없습니다. 외부 쿼리에서 반환 된 각 행에 대해 동일한 결과를 제공합니다. MariaDB에서도 비슷한 예제가 구현되어 있으며 지금까지 성능이 저하되지 않았 으므로이 SELECT *래핑이 필요할 때 명확하게보고 싶습니다 .
sbnc.eu

6

하위 쿼리가 각 행마다 실행되고 있다고 의심했습니다.
quano

일부 MySQL 버전은 IN에서 인덱스를 사용하지 않습니다. 다른 링크를 추가했습니다.
edze

1
MySQL 6은 아직 안정적이지 않으므로 프로덕션에는 권장하지 않습니다!
Johan

1
나는 그것을 추천하지 않을 것입니다. 그러나 내부에서 어떻게 실행되는지 설명합니다 (4.1 / 5.x-> 6). 이것은 현재 버전의 함정을 보여줍니다.
edze

5
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;

내 데이터베이스 중 하나에서 쿼리를 시도했으며 하위 쿼리에 대한 조인으로 다시 작성했습니다.

이것은 훨씬 빨리 작동했습니다. 사용해보십시오!


예, 그룹 결과가 포함 된 임시 테이블을 생성하므로 뷰 버전과 동일한 속도가됩니다. 그러나 쿼리 계획은 진실을 말해야합니다.
ypercubeᵀᴹ

3

이 시도

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;

2

www.prettysql.net으로 느린 SQL 쿼리를 다시 포맷했습니다.

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
불행히도 도움이되지 않습니다. 느리게 실행됩니다.
quano

답변을 업데이트했습니다. 다시 시도 할 수 있습니까? by by 그룹이 느리더라도 한 번만 실행되어야합니다.
plang

지난번에 실수로 mysql 서버를 실수로 죽였으므로 지금 시도해 볼 수 없습니다. 나중에 테스트 데이터베이스를 설정해야합니다. 그러나 이것이 왜 쿼리에 영향을 미치는지 이해하지 못합니다. HAVING 문은 그 안에있는 쿼리에만 적용해야합니다. "실제"쿼리가 하위 쿼리에 영향을 미치는 이유를 이해하지 못합니다.
quano

:이 발견 xaprb.com/blog/2006/04/30/...을 . 나는 이것이 해결책 일 것이라고 생각한다. 내가 시간이되면 시도합니다.
quano

2

먼저 중복 행을 찾고 행 수를 찾는 횟수는 다음과 같이 번호별로 정렬됩니다.

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
) 

마지막으로 중복 행을 삭제합니다 .No는 시작 0입니다. 각 그룹의 첫 번째 숫자를 제외하고 모든 중복 행을 삭제합니다.

delete from  CopyTable where No!= 0;


1

때로는 데이터가 커질 때 mysql WHERE IN은 쿼리 최적화로 인해 느려질 수 있습니다. STRAIGHT_JOIN을 사용하여 mysql에게 쿼리를 그대로 실행하도록 지시하십시오.

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

그러나 조심하십시오 : 대부분의 경우 mysql 최적화 프로그램은 꽤 잘 작동하므로 이런 종류의 문제가있을 때만 사용하는 것이 좋습니다


0

이것은라는 테이블이있는 내 경우와 유사합니다 tabel_buku_besar. 내가 필요한 것

  1. 기록을 찾고 가질 수 account_code='101.100'tabel_buku_besar있는 한 companyarea='20000'도이 IDR같은currency

  2. tabel_buku_besar1 단계와 account_code가 같지만 1 transaction_number단계 결과 가 있는 모든 레코드를 가져와야합니다.

을 사용하는 동안 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

나는 이것이 값이 존재하는지 찾는 데 가장 효율적인 것으로 판단하고, 값이 존재하지 않는지를 찾기 위해 로직을 쉽게 뒤집을 수 있습니다 (예 : 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

* 점검하려는 값의 이름으로 related_field를 바꾸십시오 테이블에 존재합니다

* 기본 키를 비교 테이블에서 기본 키 열의 이름으로 바꾸십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.