Làm cách nào để chọn các hàng có dấu thời gian gần đây nhất cho mỗi giá trị khóa?


86

Tôi có một bảng dữ liệu cảm biến. Mỗi hàng có một id cảm biến, một dấu thời gian và các trường khác. Tôi muốn chọn một hàng có dấu thời gian mới nhất cho từng cảm biến, bao gồm một số trường khác.

Tôi nghĩ rằng giải pháp sẽ là nhóm theo id cảm biến và sau đó sắp xếp theo tối đa (dấu thời gian) như vậy:

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

Điều này khiến tôi gặp lỗi khi nói rằng "sensorField1 phải xuất hiện trong nhóm theo mệnh đề hoặc được sử dụng trong một tổng thể."

Cách chính xác để tiếp cận vấn đề này là gì?


1
Bạn đang sử dụng công cụ DB nào?
juergen d

1
Mặc dù các câu trả lời bên dưới bằng cách sử dụng JOIN trên giá trị Max (dấu thời gian) sẽ hoạt động, tôi khuyên bạn nên tham gia trên SensorReadingId nếu bạn có một trên sensorTable.
Thomas Langston

Câu trả lời:


94

Để hoàn thiện, đây là một giải pháp khả thi khác:

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;

Tôi nghĩ là khá tự giải thích, nhưng đây là thông tin thêm nếu bạn muốn, cũng như các ví dụ khác. Đó là từ hướng dẫn sử dụng MySQL, nhưng truy vấn trên hoạt động với mọi RDBMS (triển khai tiêu chuẩn sql'92).


56

Điều này có thể được thực hiện một cách tương đối thanh lịch bằng cách sử dụng SELECT DISTINCT, như sau:

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

Ở trên hoạt động cho PostgreSQL (một số thông tin thêm ở đây ) nhưng tôi nghĩ cũng có các công cụ khác. Trong trường hợp không rõ ràng, điều này sẽ làm là sắp xếp bảng theo ID cảm biến và dấu thời gian (mới nhất đến cũ nhất), sau đó trả về hàng đầu tiên (tức là dấu thời gian mới nhất) cho mỗi ID cảm biến duy nhất.

Trong trường hợp sử dụng của tôi, tôi có ~ 10 triệu lần đọc từ ~ 1K cảm biến, vì vậy việc cố gắng tham gia bảng với chính nó trên bộ lọc dựa trên dấu thời gian là rất tốn tài nguyên; ở trên mất một vài giây.


Giải pháp này thực sự nhanh chóng.
Ena

Nhanh chóng và dễ hiểu. Cảm ơn bạn đã giải thích trường hợp sử dụng, vì của tôi là khá giống nhau.
Stef Verdonk

Thật không may, điều này không làm việc cho MySQL ( link )
silentsurfer

21

Bạn có thể tham gia bảng với chính nó (trên id cảm biến) và thêm left.timestamp < right.timestamplàm điều kiện tham gia. Sau đó, bạn chọn các hàng, nơi right.idnull. Thì đấy, bạn đã nhận được mục nhập mới nhất cho mỗi cảm biến.

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)

Nhưng xin lưu ý rằng điều này sẽ rất tốn tài nguyên nếu bạn có một ít id và nhiều giá trị! Vì vậy, tôi sẽ không đề xuất điều này cho một số loại Đo lường-Nội dung, trong đó mỗi Cảm biến thu thập một giá trị mỗi phút. Tuy nhiên, trong Trường hợp sử dụng, nơi bạn cần theo dõi "Bản sửa đổi" của một thứ gì đó chỉ thay đổi "đôi khi", thật dễ dàng.


Điều này nhanh hơn các câu trả lời khác, ít nhất là trong trường hợp của tôi.
rain_

@rain_ Nó thực sự phụ thuộc vào trường hợp sử dụng. Do đó, không có "câu trả lời phổ quát" cho câu hỏi này.
dognose

19

Bạn chỉ có thể chọn các cột có trong nhóm hoặc được sử dụng trong một hàm tổng hợp. Bạn có thể sử dụng một tham gia để làm cho điều này hoạt động

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

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

Tôi nghĩ rằng "LEFT JOIN" cũng được áp dụng, không chỉ "INNER JOIN"; và một phần "và s1.timestamp = s2.mts" không phải là IMHO cần thiết. Chưa hết, tôi khuyên bạn nên tạo chỉ mục trên hai trường: sensorID + timestamp - tốc độ truy vấn tăng rất nhanh!
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

Có một câu trả lời phổ biến mà tôi chưa thấy ở đây, đó là Chức năng cửa sổ. Nó là một sự thay thế cho truy vấn phụ tương quan, nếu DB của bạn hỗ trợ nó.

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;

Tôi thực sự sử dụng điều này nhiều hơn các truy vấn phụ tương quan. Hãy thoải mái làm phiền tôi trong những bình luận về tính hiệu quả, tôi không quá chắc chắn về vấn đề đó nó xếp chồng lên nhau như thế nào.


0

Tôi hầu hết đã gặp cùng một vấn đề và kết thúc một giải pháp khác khiến loại vấn đề này trở nên tầm thường để truy vấn.

Tôi có một bảng dữ liệu cảm biến (dữ liệu 1 phút từ khoảng 30 cảm biến)

SensorReadings->(timestamp,value,idSensor)

và tôi có một bảng cảm biến có rất nhiều nội dung chủ yếu là tĩnh về cảm biến nhưng các trường liên quan là:

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

TvLastupdate và tvLastValue được đặt trong một trình kích hoạt khi chèn vào bảng SensorReadings. Tôi luôn có quyền truy cập trực tiếp vào các giá trị này mà không cần thực hiện bất kỳ truy vấn đắt tiền nào. Điều này không chuẩn hóa một chút. Truy vấn là tầm thường:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

Tôi sử dụng phương pháp này cho dữ liệu được truy vấn thường xuyên. Trong trường hợp của tôi, tôi có một bảng cảm biến và một bảng sự kiện lớn, có dữ liệu đến ở cấp độ phút VÀ hàng chục máy đang cập nhật trang tổng quan và đồ thị với dữ liệu đó. Với kịch bản dữ liệu của tôi, phương pháp kích hoạt và bộ nhớ cache hoạt động tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.