Açıkçası, aynı sonuçları almanın birçok farklı yolu vardır, sorunuz MySQL'deki her grupta son sonuçları almanın etkili bir yolu gibi görünüyor. Çok miktarda veriyle çalışıyorsanız ve InnoDB'yi MySQL'in en son sürümleriyle (5.7.21 ve 8.0.4-rc gibi) bile kullandığınızı varsayarsanız, bunu yapmanın etkili bir yolu olmayabilir.
Bazen bunu 60 milyondan fazla sıra içeren tablolarla yapmamız gerekir.
Bu örnekler için, sorguların verilerdeki tüm gruplar için sonuç bulması gereken yalnızca yaklaşık 1,5 milyon satır içeren verileri kullanacağım. Gerçek vakalarımızda genellikle yaklaşık 2.000 gruptan veri geri döndürmemiz gerekir (bu varsayımsal olarak verilerin çok fazla incelenmesini gerektirmez).
Aşağıdaki tabloları kullanacağım:
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));
Sıcaklık tablosu yaklaşık 1,5 milyon rasgele kayıt ve 100 farklı grupla doldurulur. Seçilen_grup bu 100 grupla doldurulur (bizim durumumuzda bu normalde tüm gruplar için% 20'den az olacaktır).
Bu veriler rasgele olduğu için, birden çok satırın aynı kaydedilmiş zaman damgalarına sahip olabileceği anlamına gelir. İstediğimiz, her grup için son kaydedilmişTimestamp değerine sahip grup kimliği sırasına göre seçilen tüm grupların bir listesini almak ve aynı grupta bunun gibi birden fazla eşleşen satır varsa, bu satırların son eşleşen kimliği varsa.
Varsayımsal olarak MySQL, özel bir ORDER BY deyiminde son satırdaki değerleri döndüren bir last () işlevine sahipse, bunu yapabiliriz:
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;
normal GROUP BY işlevlerinden hiçbirini kullanmadığı için bu durumda yalnızca birkaç 100 satırı incelemesi gerekir. Bu 0 saniye içinde yürütülür ve bu nedenle yüksek verimli olur. Normalde MySQL'de GROUP BY yan tümcesini izleyen ORDER BY yan tümcesi görecektik, ancak bu ORDER BY yan tümcesi son () işlevi için ORDER öğesini belirlemek için kullanılır, GROUP BY öğesinden sonra olsaydı GRUPLAR sipariş ederdi. GROUP BY deyimi yoksa, döndürülen tüm satırlarda son değerler aynı olur.
Ancak MySQL buna sahip değildir, bu yüzden sahip olduklarına dair farklı fikirlere bakalım ve bunların hiçbirinin etkili olmadığını kanıtlayalım.
örnek 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
);
Bu, 3.009.254 satırı inceledi ve 5.7.21'de ~ 0.859 saniye ve 8.0.4-rc'de biraz daha uzun sürdü
ÖRNEK 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;
Bu, 1.505.331 satırı inceledi ve 5.7.21'de ~ 1.25 saniye ve 8.0.4-rc'de biraz daha uzun sürdü
ÖRNEK 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;
Bu, 3.009.685 satırı inceledi ve 5.7.21'de ~ 1.95 saniye ve 8.0.4-rc'de biraz daha uzun sürdü
Örnek 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
)
);
Bu, 6.137.810 satırı inceledi ve 5.7.21'de ~ 2.2 saniye ve 8.0.4-rc'de biraz daha uzun sürdü
Örnek 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;
Bu, 6.017.808 satırı inceledi ve 8.0.4-rc'de ~ 4.2 saniye sürdü.
ÖRNEK 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;
Bu, 6.017.908 satırı inceledi ve 8.0.4-rc'de ~ 17.5 saniye sürdü
ÖRNEK 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;
Bu sonsuza dek sürüyordu, bu yüzden onu öldürmek zorunda kaldım.