MySQL : 내부 쿼리에서 "ORDER BY"를 사용하여 UNION 최적화


9

방금 동일한 레이아웃을 가진 여러 테이블로 구성된 로깅 시스템을 설정했습니다.

각 데이터 소스마다 하나의 테이블이 있습니다.

로그 뷰어의 경우

  • UNION 모든 로그 테이블 ,
  • 계정별로 필터링 ,
  • 소스 식별을위한 의사 열 추가
  • 시간이별로 정렬 ,
  • 페이지 매김을 위해 그들을 제한 .

모든 테이블에는 zeitpunkt색인화 된 날짜 / 시간 열 이라는 필드 가 있습니다.

나의 첫번째 시도는 :

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730)

ORDER BY zeit DESC LIMIT 10;

두 테이블의 모든 행이 서브 쿼리에 의해 리턴되고 UNION.

내 해결 방법은 다음과 같습니다.

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

ORDER BY zeit DESC LIMIT 10;

쿼리 엔진이 여기에서 인덱스를 사용하기를 기대하고 있었기 때문에 두 하위 쿼리가 모두에 앞서 정렬되고 제한되어야하기 UNION때문에 행을 병합하고 정렬합니다.

나는 이것이 사실이라고 생각했지만 EXPLAIN쿼리에서 실행 하면 하위 쿼리가 여전히 두 테이블을 모두 검색한다는 것을 알 수 있습니다.

EXPLAINing하위 쿼리 자체는 원하는 최적화를 보여 주지만 UNIONing함께하지는 않습니다.

내가 뭘 놓 쳤니?

하위 쿼리 ORDER BY내부의 절 UNION은가 없으면 무시 LIMIT되지만 한계가 있음을 알고 있습니다.

편집 :
실제로account_id조건이없는 쿼리도있을 것입니다.

테이블이 이미 존재하며 데이터로 채워져 있습니다. 소스에 따라 레이아웃이 변경 될 수 있으므로 분할하여 유지하고 싶습니다. 또한 로깅 클라이언트는 다른 이유로 다른 자격 증명을 사용합니다.

로그 리더와 실제 테이블 사이에 일종의 계층을 유지해야합니다.

다음은 전체 쿼리와 첫 번째 하위 쿼리 및 테이블 레이아웃에 대한 실행 계획입니다.

https://gist.github.com/ca8fc1093cd95b1c6fc0


1
이에 대한 가장 좋은 지수는 화합물 (account_id, zeitpunkt)입니다. 그런 색인이 있습니까? 두 번째로 가장 좋은 방법은 싱글이라고 생각 (zeitpunkt)하지만 사용되는 경우 효율성은 행이 account_id=730나타나는 빈도에 따라 다릅니다 .
ypercubeᵀᴹ

2
UNION DISTINCT? 추가 식별 열로 인해 서브 쿼리마다 결과가 다르므로 정렬과 구별을 강제 할 필요가 없습니다. 사용하십시오 UNION ALL.
ypercubeᵀᴹ

1
@ypercube의 제안 외에도 질문이 있습니다. source열을 추가하여 모든 로그를 동일한 테이블에 두는 것이 낫지 않습니까? 이렇게하면 UNION모든 데이터에서을 피하고 색인을 사용할 수 있습니다.
dezso

1
@ypercube 실제로 account_id 조건이 없는 쿼리도있을 것입니다 . DISTINCT 플래그는 이전의 시도의 잔존이며, 결과는 항상 다를 수 있기 때문에 실제로 쓸모 때문에 DISTINCT는 dafualt 동작입니다. 테이블이 이미 존재하며 데이터로 채워져 있습니다. 어쨌든 소스에 따라 레이아웃이 변경되어 분할하여 유지하고 싶습니다. 또한 로깅 클라이언트는 다른 이유로 다른 자격 증명을 사용합니다. 로그 리더와 실제 테이블 사이에 일종의 계층을 유지해야합니다.
루카스

OK, 그러나 변경하여 UNION ALL다른 실행 계획 이 산출 되는지 확인하십시오 .
ypercubeᵀᴹ

답변:


8

호기심 때문에이 버전을 사용해 볼 수 있습니까? 하위 쿼리가 별도로 사용하는 것과 동일한 인덱스를 사용하도록 최적화 프로그램을 속일 수 있습니다.

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10) 
    AS a

UNION ALL

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)
    AS b

ORDER BY zeit DESC LIMIT 10;

나는 아직도 당신이 가질 수있는 가장 좋은 지수는 화합물이라고 생각합니다 (account_id, zeitpunkt). 그것은 10 행을 빨리 산출 할 것이고, 트릭이 필요하지 않을 것입니다.


수정 결과 원하는 결과가 나왔습니다. 감사! 참고로 : 지금까지 어느 인덱스가 더 나을지 확실하지 않습니다. 나는 둘 다 사용할 수 있습니다. 사용자 수와 log entries / user의지가 어떻게 확장되는지 확인해야합니다 .
루카스

가 있거나없는 쿼리가 필요한 경우 account_id=?둘 다 유지하십시오.
ypercubeᵀᴹ

@ ypercube, +1 이것은 매우 영리하며 내 (유사한) 상황에서도 일했습니다! 통합 쿼리를 더미로 래핑하여 SELECT * FROMMySQL을 인덱스를 사용하는 이유를 설명 할 수 있습니까 ?
dkamins

@ dkamins : MySQL 옵티마이 저는 그리 영리하지 않습니다. 일반적으로 여기와 같은 파생 테이블이있을 때 (SELECT ...) AS a파생 테이블을 다른 파생 테이블과 별도로 쿼리 한 다음 전체 쿼리를 평가하고 최적화하려고합니다.
ypercubeᵀᴹ

@Lukas, 실제로 인덱스가 사용되도록해야하기 때문에 사용 / 추가 force index하면 더 나은 솔루션을 얻을 수 있습니다.
Pacerier
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.