Her bir anahtar değer için en son zaman damgasına sahip satırları nasıl seçebilirim?


88

Bir sensör verisi tablom var. Her satırın bir sensör kimliği, zaman damgası ve diğer alanları vardır. Diğer alanlardan bazıları dahil olmak üzere her sensör için en son zaman damgasını içeren tek bir satır seçmek istiyorum.

Çözümün sensör kimliğine göre gruplandırmak ve ardından maks. (Zaman damgası) şeklinde sıralamak olacağını düşündüm:

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

Bu bana "sensorField1'in grupta cümlece görünmesi veya bir toplamda kullanılması gerektiğini" söyleyen bir hata veriyor.

Bu soruna yaklaşmanın doğru yolu nedir?


1
Hangi DB motorunu kullanıyorsunuz?
juergen d

1
Max (zaman damgası) değerinde JOIN'leri kullanan aşağıdaki cevaplar işe yarasa da, sensorTable'da varsa bir SensorReadingId'e katılmanızı öneririm.
Thomas Langston

Yanıtlar:


94

Eksiksizlik adına, işte başka bir olası çözüm:

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;

Oldukça kendi kendini açıklayan sanırım, ama isterseniz burada daha fazla bilgi ve diğer örnekler. MySQL kılavuzundandır, ancak yukarıdaki sorgu her RDBMS ile çalışır (sql'92 standardını uygular).


57

Bu, SELECT DISTINCTaşağıdaki gibi nispeten zarif bir şekilde yapılabilir :

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

Yukarıdakiler PostgreSQL için çalışıyor (biraz daha fazla bilgi burada ) ama diğer motorları da düşünüyorum. Açık olmadığı takdirde, bunun yaptığı şey, tabloyu sensör kimliği ve zaman damgasına (en yeniden en eskiye) göre sıralamak ve ardından her benzersiz sensör kimliği için ilk satırı (yani en son zaman damgasını) döndürmektir.

Benim kullanım durumumda ~ 1K sensörden ~ 10M okumaya sahibim, bu yüzden tabloyu zaman damgasına dayalı bir filtrede kendisiyle birleştirmeye çalışmak çok kaynak yoğun; yukarıdakiler birkaç saniye sürer.


Bu çözüm gerçekten hızlı.
Ena

Hızlı ve anlaşılması kolay. Benimki oldukça benzer olduğu için kullanım durumunu da açıkladığınız için teşekkürler.
Stef Verdonk

1
Maalesef bu MySQL ( bağlantı ) için geçerli değil
silentsurfer

21

Masayı kendisiyle (sensör kimliğiyle) left.timestamp < right.timestampbirleştirebilir ve birleştirme koşulu olarak ekleyebilirsiniz . Sonra nerede right.idolduğu satırları seçersiniz null. Voila, sensör başına en son girişi aldınız.

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)

Ancak, eğer az miktarda kimliğiniz ve çok sayıda değeriniz varsa, bunun çok yoğun kaynak gerektireceğini lütfen unutmayın! Bu nedenle, bunu her Sensörün her dakika bir değer topladığı bir tür Ölçüm İşlemi için önermem. Bununla birlikte, sadece "bazen" değişen bir şeyin "Revizyonlarını" izlemeniz gereken bir Kullanım Durumunda, kolaydır.


Bu, en azından benim durumumda diğer cevaplardan daha hızlı.
rain_

@rain_ Gerçekten kullanım durumuna bağlı. Bu nedenle, bu sorunun "evrensel bir cevabı" yoktur.
dognose

19

Yalnızca gruptaki veya bir toplama işlevinde kullanılan sütunları seçebilirsiniz. Bunu çalıştırmak için bir birleşim kullanabilirsiniz

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

... veya select * from sensorTable where (sensorID, timestamp) in (select sensorID, max(timestamp) from sensorTable group by sensorID).
Arjan

Bence "LEFT JOIN" de uygulanıyor, sadece "INNER JOIN" değil; ve "ve s1.timestamp = s2.mts" bölümü gerekli IMHO değildir. Yine de, iki alanda dizin oluşturmanızı öneririm: sensorID + zaman damgası - sorgu hızı harika artar!
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

2

Henüz burada görmediğim tek bir ortak cevap var, o da Pencere Fonksiyonu. DB'niz destekliyorsa, ilişkili alt sorguya bir alternatiftir.

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;

Aslında bunu ilişkili alt sorgulardan daha fazla kullanıyorum. Verimlilik konusundaki yorumlarda beni rahatsız etmekten çekinmeyin, bu konuda nasıl yığıldığından pek emin değilim.


0

Çoğunlukla aynı sorunu yaşadım ve bu tür sorunları sorgulamak için önemsiz hale getiren farklı bir çözüm buldum.

Bir sensör verisi tablom var (yaklaşık 30 sensörden 1 dakikalık veri)

SensorReadings->(timestamp,value,idSensor)

ve sensörle ilgili çoğunlukla statik şeyler içeren bir sensör masam var ancak ilgili alanlar şunlar:

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

TvLastupdate ve tvLastValue, SensorReadings tablosuna yapılan eklemelerde bir tetikleyicide ayarlanır. Pahalı sorgulamalara gerek kalmadan bu değerlere her zaman doğrudan erişime sahibim. Bu biraz denormalize olur. Sorgu önemsizdir:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

Bu yöntemi sıklıkla sorgulanan veriler için kullanıyorum. Benim durumumda, dakika düzeyinde gelen verilere sahip bir sensör masam ve büyük bir olay tablosu var VE düzinelerce makine bu verilerle gösterge tablolarını ve grafikleri güncelliyor. Veri senaryomda tetikleme ve önbellek yöntemi iyi çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.