Bagaimana cara memilih baris dengan stempel waktu terbaru untuk setiap nilai kunci?

88

Saya memiliki tabel data sensor. Setiap baris memiliki id sensor, stempel waktu, dan bidang lainnya. Saya ingin memilih satu baris dengan cap waktu terbaru untuk setiap sensor, termasuk beberapa bidang lainnya.

Saya pikir solusinya adalah mengelompokkan berdasarkan id sensor dan kemudian memesan berdasarkan max (timestamp) seperti ini:

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM sensorTable 
GROUP BY sensorID 
ORDER BY max(timestamp);

Ini memberi saya kesalahan yang mengatakan bahwa "sensorField1 harus muncul di grup menurut klausa atau digunakan secara agregat".

Apa cara yang benar untuk mengatasi masalah ini?

terus terang
sumber
1
Mesin DB apa yang Anda gunakan?
juergen d
1
Sementara jawaban di bawah ini menggunakan JOINs on the Max (timestamp) nilai seharusnya berfungsi, saya akan menyarankan untuk bergabung di SensorReadingId jika Anda memilikinya di sensorTable.
Thomas Langston

Jawaban:

94

Demi kelengkapan, berikut kemungkinan solusi lain:

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM sensorTable s1
WHERE timestamp = (SELECT MAX(timestamp) FROM sensorTable s2 WHERE s1.sensorID = s2.sensorID)
ORDER BY sensorID, timestamp;

Cukup menjelaskan diri sendiri menurut saya, tetapi berikut ini info selengkapnya jika Anda mau, serta contoh lainnya. Ini dari manual MySQL, tetapi kueri di atas berfungsi dengan setiap RDBMS (menerapkan standar sql'92).

fancyPants
sumber
57

Ini dapat dilakukan dengan cara yang relatif elegan dengan menggunakan SELECT DISTINCT, sebagai berikut:

SELECT DISTINCT ON (sensorID)
sensorID, timestamp, sensorField1, sensorField2 
FROM sensorTable
ORDER BY sensorID, timestamp DESC;

Di atas berfungsi untuk PostgreSQL (beberapa info lebih lanjut di sini ) tetapi saya pikir juga mesin lain. Jika tidak jelas, yang dilakukan adalah mengurutkan tabel berdasarkan ID sensor dan stempel waktu (terbaru ke terlama), lalu mengembalikan baris pertama (yaitu stempel waktu terbaru) untuk setiap ID sensor unik.

Dalam kasus penggunaan saya, saya memiliki ~ 10 juta pembacaan dari ~ 1K sensor, jadi mencoba untuk menggabungkan tabel dengan dirinya sendiri pada filter berbasis timestamp sangat intensif sumber daya; di atas membutuhkan beberapa detik.

Svet
sumber
Solusi ini sangat cepat.
Ena
Cepat dan mudah dimengerti. Terima kasih telah menjelaskan kasus penggunaannya juga, karena kasus saya sangat mirip.
Stef Verdonk
1
Sayangnya, ini tidak berfungsi untuk MySQL ( tautan )
silentsurfer
21

Anda dapat menggabungkan tabel dengan dirinya sendiri (pada sensor id), dan menambahkan left.timestamp < right.timestampsebagai kondisi penggabungan. Kemudian Anda memilih baris, di mana right.idadalah null. Voila, Anda mendapat entri terbaru per sensor.

http://sqlfiddle.com/#!9/45147/37

SELECT L.* FROM sensorTable L
LEFT JOIN sensorTable R ON
L.sensorID = R.sensorID AND
L.timestamp < R.timestamp
WHERE isnull (R.sensorID)

Tapi harap dicatat, bahwa ini akan sangat intensif sumber daya jika Anda memiliki sedikit id dan banyak nilai! Jadi, saya tidak akan merekomendasikan ini untuk semacam Mengukur-Barang, di mana setiap Sensor mengumpulkan nilai setiap menit. Namun dalam Use-Case, di mana Anda perlu melacak "Revisi" dari sesuatu yang hanya "kadang-kadang" berubah, itu mudah.

dognose
sumber
Ini lebih cepat dari jawaban lain, setidaknya dalam kasus saya.
hujan_
@rain_ Itu sangat tergantung pada kasus penggunaan. Oleh karena itu, tidak ada "jawaban universal" untuk pertanyaan ini.
kenalkan
19

Anda hanya dapat memilih kolom yang ada di grup atau digunakan dalam fungsi agregat. Anda dapat menggunakan gabungan agar ini berfungsi

select s1.* 
from sensorTable s1
inner join 
(
  SELECT sensorID, max(timestamp) as mts
  FROM sensorTable 
  GROUP BY sensorID 
) s2 on s2.sensorID = s1.sensorID and s1.timestamp = s2.mts
juergen d
sumber
... atau select * from sensorTable where (sensorID, timestamp) in (select sensorID, max(timestamp) from sensorTable group by sensorID).
Arjan
Saya pikir "LEFT JOIN" juga diterapkan, tidak hanya "INNER JOIN"; dan bagian "dan s1.timestamp = s2.mts" tidak perlu IMHO. Namun, saya menyarankan untuk membuat indeks pada dua bidang: sensorID + stempel waktu - kecepatan kueri meningkat pesat!
Igor
4
WITH SensorTimes As (
   SELECT sensorID, MAX(timestamp) "LastReading"
   FROM sensorTable
   GROUP BY sensorID
)
SELECT s.sensorID,s.timestamp,s.sensorField1,s.sensorField2 
FROM sensorTable s
INNER JOIN SensorTimes t on s.sensorID = t.sensorID and s.timestamp = t.LastReading
Joel Coehoorn
sumber
2

Ada satu jawaban umum yang belum saya lihat di sini, yaitu Fungsi Jendela. Ini adalah alternatif untuk sub-kueri terkait, jika DB Anda mendukungnya.

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM (
    SELECT sensorID,timestamp,sensorField1,sensorField2
        , ROW_NUMBER() OVER(
            PARTITION BY sensorID
            ORDER BY timestamp
        ) AS rn
    FROM sensorTable s1
WHERE rn = 1
ORDER BY sensorID, timestamp;

Saya sebenarnya menggunakan ini lebih dari sub-kueri yang berkorelasi. Jangan ragu untuk menghina saya di komentar tentang efisiensi, saya tidak terlalu yakin bagaimana hal itu menumpuk dalam hal itu.

Jamie Marshall
sumber
0

Saya memiliki sebagian besar masalah yang sama dan akhirnya mendapatkan solusi berbeda yang membuat jenis masalah ini sepele untuk ditanyakan.

Saya memiliki tabel data sensor (data 1 menit dari sekitar 30 sensor)

SensorReadings->(timestamp,value,idSensor)

dan saya memiliki tabel sensor yang sebagian besar berisi hal-hal statis tentang sensor tetapi bidang yang relevan adalah:

Sensors->(idSensor,Description,tvLastUpdate,tvLastValue,...)

TvLastupdate dan tvLastValue disetel di pemicu saat menyisipkan ke tabel SensorReadings. Saya selalu memiliki akses langsung ke nilai-nilai ini tanpa perlu melakukan kueri yang mahal. Ini sedikit mendenormalisasi. Kueri itu sepele:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

Saya menggunakan metode ini untuk data yang sering ditanyakan. Dalam kasus saya, saya memiliki tabel sensor, dan tabel peristiwa besar, yang memiliki data yang masuk pada tingkat menit DAN lusinan mesin memperbarui dasbor dan grafik dengan data tersebut. Dengan skenario data saya, metode trigger-and-cache bekerja dengan baik.

Hucker
sumber