분명히 동일한 결과를 얻는 데는 여러 가지 방법이 있습니다. 귀하의 질문은 MySQL의 각 그룹에서 마지막 결과를 얻는 효율적인 방법 인 것 같습니다. 방대한 양의 데이터로 작업하고 있고 최신 버전의 MySQL (예 : 5.7.21 및 8.0.4-rc)에서도 InnoDB를 사용한다고 가정하면이 작업을 효율적으로 수행 할 수있는 방법이 없을 수 있습니다.
때로는 6000 만 개가 넘는 행이있는 테이블을 사용하여이 작업을 수행해야합니다.
이 예제에서는 쿼리에서 데이터의 모든 그룹에 대한 결과를 찾아야하는 행이 약 150 만 행인 데이터를 사용합니다. 실제 사례에서 우리는 종종 약 2,000 개의 그룹에서 데이터를 반환해야합니다 (가설로 많은 데이터를 검사 할 필요가 없음).
다음 테이블을 사용합니다.
CREATE TABLE temperature(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
groupID INT UNSIGNED NOT NULL,
recordedTimestamp TIMESTAMP NOT NULL,
recordedValue INT NOT NULL,
INDEX groupIndex(groupID, recordedTimestamp),
PRIMARY KEY (id)
);
CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id));
온도 표는 약 150 만 개의 무작위 레코드와 100 개의 다른 그룹으로 채워져 있습니다. selected_group은 100 개의 그룹으로 채워집니다 (이 경우 일반적으로 모든 그룹에서 20 % 미만입니다).
이 데이터는 임의이므로 여러 행에 동일한 recordedTimestamp가있을 수 있습니다. 우리가 원하는 것은 각 그룹에 대해 마지막으로 기록 된 타임 스탬프를 가진 groupID 순서대로 선택한 모든 그룹의 목록을 얻는 것입니다. 같은 그룹에 일치하는 행이 두 개 이상 있으면 해당 행의 마지막 일치하는 ID가 있습니다.
가상의 MySQL에 특별한 ORDER BY 절의 마지막 행에서 값을 반환하는 last () 함수가 있다면 간단히 다음과 같이 할 수 있습니다.
SELECT
last(t1.id) AS id,
t1.groupID,
last(t1.recordedTimestamp) AS recordedTimestamp,
last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;
이 경우 일반적인 GROUP BY 함수를 사용하지 않으므로이 경우 몇 개의 100 개 행만 검사하면됩니다. 이것은 0 초 안에 실행되므로 매우 효율적입니다. 일반적으로 MySQL에서는 GROUP BY 절 뒤에 ORDER BY 절이 표시되지만이 ORDER BY 절은 last () 함수의 ORDER를 결정하는 데 사용됩니다. GROUP BY 이후 인 경우 GROUPS를 주문합니다. GROUP BY 절이 없으면 마지막 값은 반환 된 모든 행에서 동일합니다.
그러나 MySQL에는 이것이 없기 때문에 그것이 가지고있는 것에 대한 다른 아이디어를 살펴보고 이것들 중 어느 것도 효율적이지 않다는 것을 증명합시다.
실시 예 1
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT t2.id
FROM temperature t2
WHERE t2.groupID = g.id
ORDER BY t2.recordedTimestamp DESC, t2.id DESC
LIMIT 1
);
이것은 3,009,254 행을 검사하고 5.7.21에서 ~ 0.859 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.
실시 예 2
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
INNER JOIN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
) t5 ON t5.id = t1.id;
이것은 1,505,331 행을 검사하고 5.7.21에서 ~ 1.25 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.
실시 예 3
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
WHERE t1.id IN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
)
ORDER BY t1.groupID;
이것은 3,009,685 행을 검사하고 5.7.21에서 ~ 1.95 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.
실시 예 4
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT max(t2.id)
FROM temperature t2
WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
SELECT max(t3.recordedTimestamp)
FROM temperature t3
WHERE t3.groupID = g.id
)
);
이것은 6,137,810 행을 검사하고 5.7.21에서 ~ 2.2 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.
실시 예 5
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
t2.id,
t2.groupID,
t2.recordedTimestamp,
t2.recordedValue,
row_number() OVER (
PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
) AS rowNumber
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;
이것은 6,017,808 행을 검사하고 8.0.4-rc에서 ~ 4.2 초가 걸렸습니다.
실시 예 6
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
last_value(t2.id) OVER w AS id,
t2.groupID,
last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp,
last_value(t2.recordedValue) OVER w AS recordedValue
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
WINDOW w AS (
PARTITION BY t2.groupID
ORDER BY t2.recordedTimestamp, t2.id
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
) t1
GROUP BY t1.groupID;
이것은 6,017,908 행을 검사하고 8.0.4-rc에서 ~ 17.5 초가 걸렸습니다.
실시 예 7
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2
ON t2.groupID = g.id
AND (
t2.recordedTimestamp > t1.recordedTimestamp
OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
)
WHERE t2.id IS NULL
ORDER BY t1.groupID;
이건 영원히 복용해서 죽여야 했어요