ฉันจะเลือกแถวที่มีการประทับเวลาล่าสุดสำหรับค่าคีย์แต่ละค่าได้อย่างไร


88

ฉันมีตารางข้อมูลเซ็นเซอร์ แต่ละแถวจะมีรหัสเซ็นเซอร์การประทับเวลาและช่องอื่น ๆ ฉันต้องการเลือกแถวเดียวที่มีการประทับเวลาล่าสุดสำหรับแต่ละเซ็นเซอร์รวมถึงฟิลด์อื่น ๆ ด้วย

ฉันคิดว่าวิธีแก้ปัญหาคือการจัดกลุ่มตามรหัสเซ็นเซอร์แล้วเรียงลำดับตาม max (การประทับเวลา) ดังนี้:

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

สิ่งนี้ทำให้ฉันมีข้อผิดพลาดที่บอกว่า "sensorField1 ต้องปรากฏในกลุ่มตามอนุประโยคหรือใช้ในการรวม"

วิธีที่ถูกต้องในการแก้ไขปัญหานี้คืออะไร?


1
คุณใช้ DB Engine อะไร
juergen d

1
ในขณะที่คำตอบด้านล่างโดยใช้ JOIN บนค่า Max (การประทับเวลา) ควรใช้งานได้ฉันขอแนะนำให้เข้าร่วม SensorReadingId หากคุณมีอยู่ใน sensorTable
Thomas Langston

คำตอบ:


94

เพื่อความสมบูรณ์นี่เป็นอีกวิธีหนึ่งที่เป็นไปได้:

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;

ฉันคิดว่าอธิบายตัวเองได้ค่อนข้างดีแต่นี่คือข้อมูลเพิ่มเติมหากคุณต้องการรวมถึงตัวอย่างอื่น ๆ มาจากคู่มือ MySQL แต่แบบสอบถามด้านบนใช้ได้กับ RDBMS ทุกตัว (ใช้มาตรฐาน sql'92)


57

สิ่งนี้สามารถทำได้อย่างสวยงามโดยใช้SELECT DISTINCTดังนี้:

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

ข้างต้นใช้ได้กับ PostgreSQL (ข้อมูลเพิ่มเติมบางส่วนที่นี่ ) แต่ฉันคิดว่าเอ็นจิ้นอื่น ๆ ด้วย ในกรณีที่ไม่ชัดเจนสิ่งนี้จะจัดเรียงตารางตาม ID เซ็นเซอร์และการประทับเวลา (ใหม่สุดไปเก่าสุด) จากนั้นส่งกลับแถวแรก (เช่นการประทับเวลาล่าสุด) สำหรับ ID เซ็นเซอร์แต่ละตัวที่ไม่ซ้ำกัน

ในกรณีการใช้งานของฉันฉันมีการอ่านค่า ~ 10M จากเซ็นเซอร์ ~ 1K ดังนั้นการพยายามเข้าร่วมตารางกับตัวกรองตามการประทับเวลานั้นใช้ทรัพยากรมาก ข้างต้นใช้เวลาสองถึงสามวินาที


การแก้ปัญหานี้รวดเร็วจริงๆ
Ena

เข้าใจง่ายและรวดเร็ว ขอบคุณสำหรับการอธิบายกรณีการใช้งานเช่นกันเนื่องจากของฉันค่อนข้างคล้ายกัน
Stef Verdonk

1
น่าเสียดายที่สิ่งนี้ใช้ไม่ได้กับ MySQL ( ลิงก์ )
silentsurfer

21

คุณสามารถเข้าร่วมโต๊ะด้วยตัวมันเอง (บนรหัสเซ็นเซอร์) และเพิ่มleft.timestamp < right.timestampเป็นเงื่อนไขการเข้าร่วม จากนั้นคุณสามารถเลือกแถวที่เป็นright.id nullVoila คุณมีรายการล่าสุดต่อเซ็นเซอร์

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)

แต่โปรดทราบว่านี่จะเป็นทรัพยากรที่เข้มข้นมากหากคุณมีรหัสเพียงเล็กน้อยและมีค่ามากมาย! ดังนั้นฉันจะไม่แนะนำสิ่งนี้สำหรับ Measuring-Stuff ซึ่ง Sensor แต่ละตัวจะรวบรวมค่าทุกๆนาที อย่างไรก็ตามในกรณีการใช้งานที่คุณต้องติดตาม "การแก้ไข" ของบางสิ่งที่เปลี่ยนแปลงเพียง "บางครั้ง" มันเป็นเรื่องง่าย


นี่เร็วกว่าคำตอบอื่น ๆ อย่างน้อยก็ในกรณีของฉัน
rain_

@rain_ มันขึ้นอยู่กับกรณีการใช้งานจริงๆ ดังนั้นจึงไม่มี "คำตอบสากล" สำหรับคำถามนี้
dognose

19

คุณสามารถเลือกได้เฉพาะคอลัมน์ที่อยู่ในกลุ่มหรือใช้ในฟังก์ชันรวมเท่านั้น คุณสามารถใช้การเข้าร่วมเพื่อทำงานนี้ได้

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

... หรือselect * from sensorTable where (sensorID, timestamp) in (select sensorID, max(timestamp) from sensorTable group by sensorID).
Arjan

ฉันคิดว่า "LEFT JOIN" ถูกนำไปใช้เช่นกันไม่ใช่แค่ "INNER JOIN" เท่านั้น และส่วนหนึ่ง "และ s1.timestamp = s2.mts" ไม่ใช่ IMHO ที่จำเป็น แต่ฉันแนะนำให้สร้างดัชนีในสองช่อง: sensorID + timestamp - ความเร็วในการสืบค้นเพิ่มขึ้นมาก!
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

มีคำตอบทั่วไปอย่างหนึ่งที่ฉันยังไม่เห็นที่นี่ซึ่งก็คือฟังก์ชันหน้าต่าง เป็นทางเลือกสำหรับแบบสอบถามย่อยที่สัมพันธ์กันหาก DB ของคุณรองรับ

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;

ฉันใช้สิ่งนี้มากกว่าการสืบค้นย่อยที่สัมพันธ์กัน อย่าลังเลที่จะจับฉันในความคิดเห็นเกี่ยวกับความมีประสิทธิภาพฉันไม่แน่ใจว่ามันซ้อนกันอย่างไรในเรื่องนั้น


0

ฉันมีปัญหาเดียวกันเป็นส่วนใหญ่และลงเอยด้วยวิธีแก้ปัญหาอื่นที่ทำให้ปัญหาประเภทนี้ไม่สำคัญในการสืบค้น

ฉันมีตารางข้อมูลเซ็นเซอร์ (ข้อมูล 1 นาทีจากเซ็นเซอร์ประมาณ 30 ตัว)

SensorReadings->(timestamp,value,idSensor)

และฉันมีตารางเซ็นเซอร์ที่มีส่วนใหญ่คงที่เกี่ยวกับเซ็นเซอร์ แต่ช่องที่เกี่ยวข้องมีดังนี้:

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

tvLastupdate และ tvLastValue ถูกตั้งค่าไว้ในทริกเกอร์ที่แทรกไปยังตาราง SensorReadings ฉันสามารถเข้าถึงค่าเหล่านี้ได้โดยตรงโดยไม่ต้องทำแบบสอบถามราคาแพงใด ๆ สิ่งนี้ทำให้เกิดความผิดปกติเล็กน้อย คำถามเป็นเรื่องเล็กน้อย:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

ฉันใช้วิธีนี้สำหรับข้อมูลที่ถูกสอบถามบ่อย ในกรณีของฉันฉันมีตารางเซ็นเซอร์และตารางเหตุการณ์ขนาดใหญ่ที่มีข้อมูลเข้ามาในระดับนาทีและเครื่องหลายสิบเครื่องกำลังอัปเดตแดชบอร์ดและกราฟด้วยข้อมูลนั้น ด้วยสถานการณ์ข้อมูลของฉันเมธอด trigger-and-cache ทำงานได้ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.