그렇게하는 방법?
이 질문의 이전 제목은 ” using rank (@Rank : = @Rank + 1) in complex query with subqueries-will it work? “였습니다. 왜냐하면 순위를 사용하여 솔루션을 찾고 있었기 때문입니다. 훨씬 낫습니다.
원래 질문 :
정의 된 순서가 주어지면 각 그룹에서 마지막 레코드를 가져 오는 쿼리를 작성하려고합니다.
SET @Rank=0;
select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from Table
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from Table
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField
식은 @Rank := @Rank + 1
일반적으로 순위에 사용되지만 나에게는 두 개의 하위 쿼리에서 사용하면 의심 스럽지만 한 번만 초기화됩니다. 이런 식으로 작동합니까?
둘째, 여러 번 평가되는 하나의 하위 쿼리와 함께 작동합니까? where (or having) 절의 하위 쿼리와 유사합니다 (위를 작성하는 또 다른 방법).
SET @Rank=0;
select Table.*, @Rank := @Rank + 1 AS Rank
from Table
having Rank = (select max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from Table as t0
order by OrderField
) as t
where t.GroupId = table.GroupId
)
order by OrderField
미리 감사드립니다!
답변
따라서 OrderField
그룹당 가장 높은 행을 얻고 싶 습니까? 나는 이렇게 할 것입니다.
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId AND t1.OrderField < t2.OrderField
WHERE t2.GroupId IS NULL
ORDER BY t1.OrderField; // not needed! (note by Tomas)
( Tomas 편집 : 동일한 그룹 내에 동일한 OrderField를 가진 레코드가 더 있고 그중 정확히 하나가 필요한 경우 조건을 확장 할 수 있습니다.
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId
AND (t1.OrderField < t2.OrderField
OR (t1.OrderField = t2.OrderField AND t1.Id < t2.Id))
WHERE t2.GroupId IS NULL
편집 끝.)
즉, 동일 하고 더 큰 t1
다른 행 t2
이 존재 하지 않는 행 을 반환합니다 . 때 NULL, 그것은 왼쪽 외부 그러한 일치를 발견 조인 의미, 따라서 의 가장 큰 값이 그룹에 있습니다.GroupId
OrderField
t2.*
t1
OrderField
순위도, 하위 쿼리도 없습니다. 이것은 빠르게 실행되어야하며에 복합 인덱스가있는 경우 “Using index”를 사용하여 t2에 대한 액세스를 최적화해야합니다 (GroupId, OrderField)
.
성능과 관련 하여 각 그룹의 마지막 레코드 검색에 대한 내 답변을 참조하십시오 . Stack Overflow 데이터 덤프를 사용하여 하위 쿼리 방법과 조인 방법을 시도했습니다. 차이점은 놀랍습니다. 조인 방법은 테스트에서 278 배 더 빠르게 실행되었습니다.
최상의 결과를 얻으려면 올바른 인덱스가 있어야합니다!
@Rank 변수를 사용하는 방법은 쿼리가 첫 번째 테이블을 처리 한 후 @Rank 값이 0으로 재설정되지 않기 때문에 작성한대로 작동하지 않습니다. 예를 하나 보여 드리겠습니다.
그룹당 가장 큰 행을 제외하고는 null 인 추가 필드와 함께 더미 데이터를 삽입했습니다.
select * from `Table`;
+---------+------------+------+
| GroupId | OrderField | foo |
+---------+------------+------+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+---------+------------+------+
순위가 첫 번째 그룹의 경우 3, 두 번째 그룹의 경우 6으로 증가하고 내부 쿼리가 다음을 올바르게 반환 함을 보여줄 수 있습니다.
select GroupId, max(Rank) AS MaxRank
from (
select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField) as t
group by GroupId
+---------+---------+
| GroupId | MaxRank |
+---------+---------+
| 10 | 3 |
| 20 | 6 |
+---------+---------+
이제 조인 조건없이 쿼리를 실행하여 모든 행의 카티 전 곱을 적용하고 모든 열도 가져옵니다.
select s.*, t.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
-- on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+---------+---------+---------+------------+------+------+
| GroupId | MaxRank | GroupId | OrderField | foo | Rank |
+---------+---------+---------+------------+------+------+
| 10 | 3 | 10 | 10 | NULL | 7 |
| 20 | 6 | 10 | 10 | NULL | 7 |
| 10 | 3 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 30 | foo | 9 |
| 10 | 3 | 10 | 30 | foo | 9 |
| 10 | 3 | 20 | 40 | NULL | 10 |
| 20 | 6 | 20 | 40 | NULL | 10 |
| 10 | 3 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 60 | foo | 12 |
| 10 | 3 | 20 | 60 | foo | 12 |
+---------+---------+---------+------------+------+------+
위에서 볼 수 있듯이 그룹당 최대 순위는 정확하지만 두 번째 파생 테이블을 처리함에 따라 @Rank는 7 이상으로 계속 증가합니다. 따라서 두 번째 파생 테이블의 순위는 첫 번째 파생 테이블의 순위와 전혀 겹치지 않습니다.
두 테이블을 처리하는 사이에 @Rank를 0으로 재설정하도록 다른 파생 테이블을 추가해야합니다 (최적화 프로그램이 테이블을 평가하는 순서를 변경하지 않기를 바랍니다. 그렇지 않으면 STRAIGHT_JOIN을 사용하여이를 방지합니다).
select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (select @Rank := 0) r -- RESET @Rank TO ZERO HERE
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+---------+------------+------+------+
| GroupId | OrderField | foo | Rank |
+---------+------------+------+------+
| 10 | 30 | foo | 3 |
| 20 | 60 | foo | 6 |
+---------+------------+------+------+
그러나이 쿼리의 최적화는 끔찍합니다. 인덱스를 사용할 수없고 두 개의 임시 테이블을 만들고 어려운 방식으로 정렬하며 임시 테이블을 조인 할 때 인덱스를 사용할 수 없기 때문에 조인 버퍼도 사용합니다. 다음은 다음의 출력 예입니다 EXPLAIN
.
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| 1 | PRIMARY | <derived4> | system | NULL | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | |
| 1 | PRIMARY | <derived5> | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using join buffer |
| 5 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 4 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary; Using filesort |
| 3 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
왼쪽 외부 조인을 사용하는 내 솔루션은 훨씬 더 최적화됩니다. 임시 테이블과 보고서 "Using index"
를 사용하지 않으므로 데이터를 건드리지 않고 인덱스 만 사용하여 조인을 해결할 수 있습니다.
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 1 | SIMPLE | t2 | ref | GroupId | GroupId | 5 | test.t1.GroupId | 1 | Using where; Using index |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
당신은 아마도 그들의 블로그에서 “조인하면 SQL이 느리게 만든다”고 주장하는 사람들을 읽을 것입니다. 그러나 그것은 말도 안됩니다. 최적화가 제대로 이루어지지 않으면 SQL이 느려집니다.