2009-01-30 29 views
49

Tôi có dữ liệu mà trông như thế này:Làm cách nào để tham gia hàng gần đây nhất trong một bảng đến một bảng khác?

entities 
id   name 
1   Apple 
2   Orange 
3   Banana 

Định kỳ, một quá trình sẽ chạy và cho điểm cho từng đối tượng. Quá trình này tạo ra các dữ liệu và thêm nó vào một bảng điểm như sau:

scores 
id entity_id score date_added 
1 1   10  1/2/09 
2 2   10  1/2/09 
3 1   15  1/3/09 
4 2   10  1/03/09 
5 1   15  1/4/09 
6 2   15  1/4/09 
7 3   22  1/4/09 

Tôi muốn để có thể chọn tất cả các thực thể cùng với số điểm ghi nhận gần đây nhất cho mỗi kết quả là một số dữ liệu như thế này:

entities 
id name  score date_added 
1 Apple  15  1/4/09 
2 Orange 15  1/4/09 
3 Banana 15  1/4/09 

tôi có thể lấy dữ liệu cho một thực thể duy nhất sử dụng truy vấn này:

SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

WHERE entities.id = ? 

ORDER BY scores.date_added DESC 
LIMIT 1 

Nhưng tôi đang ở một mất mát về cách chọn giống nhau cho tất cả các thực thể. Có lẽ nó đang nhìn chằm chằm vào mặt tôi?

Cảm ơn bạn rất vui vì đã dành thời gian.

Cảm ơn các câu trả lời tuyệt vời. Tôi sẽ cung cấp cho nó một vài ngày để xem nếu một giải pháp ưa thích bong bóng lên sau đó tôi sẽ chọn câu trả lời.

CẬP NHẬT: Tôi đã thử một số giải pháp được đề xuất, vấn đề chính mà tôi đang gặp phải là nếu một thực thể chưa có số điểm được tạo, chúng sẽ không xuất hiện trong danh sách.

SQL sẽ trông như thế nào để đảm bảo rằng tất cả các thực thể được trả lại, ngay cả khi chúng chưa có điểm số nào được đăng?

CẬP NHẬT: Đã chọn câu trả lời. Cảm ơn mọi người!

Trả lời

60

tôi làm điều đó theo cách này:

SELECT e.*, s1.score, s1.date_added 
FROM entities e 
    INNER JOIN scores s1 
    ON (e.id = s1.entity_id) 
    LEFT OUTER JOIN scores s2 
    ON (e.id = s2.entity_id AND s1.id < s2.id) 
WHERE s2.id IS NULL; 
+1

Cảm ơn Bill, tôi đã kết thúc giải quyết giải pháp này nhưng đổi chỗ INNER JOIN cho LEFT JOIN để bao gồm các thực thể chưa có điểm số. – GloryFish

+1

Tôi thích giải pháp này, tôi cũng đang sử dụng LEFT JOIN. Làm thế nào bạn sẽ đề nghị đối phó với một tie, trong trường hợp có hai điểm cho cùng một thực thể với cùng một ngày? – russds

+0

@russds, sử dụng cột khác để giải quyết các mối quan hệ. –

1
SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

WHERE entities.id in 
(select id from scores s2 where date_added = max(date_added) and s2.id = entities.id) 

ORDER BY scores.date_added DESC 
LIMIT 1 
+0

Truy vấn phụ của bạn đang sử dụng cột (date_added) không tồn tại trong bảng mà bạn đang truy vấn. –

5

cách tiếp cận 1

SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

WHERE scores.date_added = 
    (SELECT max(date_added) FROM scores where entity_id = entities.id) 
+0

Hiệu suất tốt nhất (cho đến nay) nếu [điểm] được đánh chỉ mục bởi [entity_id] – MatBailie

+0

Trong bài kiểm tra của tôi, giải pháp này dường như trả lại nhiều hàng cho bất kỳ thực thể nào có nhiều hơn một điểm được thêm vào đối tượng đó vào cùng ngày (mới nhất). – beporter

+0

Ngoài ra, nó sẽ bỏ qua các hàng không có điểm số. – Coleman

3

cách tiếp cận 2

chi phí truy vấn liên quan đến hàng loạt:

012.351.
SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

inner join 
    (
    SELECT 
      entity_id, max(date_added) as recent_date 
    FROM scores 
    group by entity_id 
    ) as y on entities.id = y.entity_id and scores.date_added = y.recent_date 
9

Chỉ cần thêm sự thay đổi của tôi trên nó:

SELECT e.*, s1.score 
FROM entities e 
INNER JOIN score s1 ON e.id = s1.entity_id 
WHERE NOT EXISTS (
    SELECT 1 FROM score s2 WHERE s2.id > s1.id 
) 
+3

Tôi thích cái này! Ít nhất là trên SQL Server, điều này sẽ chạy rất nhanh. Bây giờ, tôi khuyên bạn nên thay đổi INNER cho một LEFT JOIN, chỉ trong trường hợp một thực thể mới vừa được thêm vào và quá trình này vẫn chưa chạy. –

+1

Để tăng tốc độ, bạn có thể đặt kiểm tra sự tồn tại như là một phần của điều kiện kết nối. Ít nhất là trên SQL S. chúng được thực hiện trước khi lọc WHERE được thực hiện, vì vậy bạn sẽ tiết kiệm một vài phần nghìn giây mỗi hàng bằng cách cắt tỉa tìm kiếm của bạn ở đó. –

+0

Hmmmm, WHERE lọc không nhất thiết phải thực hiện sau khi JOIN mệnh đề. Trong thực tế, họ có thể được thực hiện đầu tiên, đặc biệt là nếu mệnh đề WHERE bộ lọc trên một INDEX ... – MatBailie

2

Tôi biết đây là một câu hỏi cũ, chỉ cần nghĩ rằng tôi muốn thêm một cách tiếp cận không ai có đề cập nào, Cross Apply hay Outer Apply. Đây là có sẵn trong SQL Server 2005 (loại cơ sở dữ liệu không được gắn thẻ trong câu hỏi này) Hoặc cao hơn

Sử dụng các bảng tạm thời

DECLARE @Entities TABLE(Id INT PRIMARY KEY, name NVARCHAR(MAX)) 
INSERT INTO @Entities 
VALUES (1, 'Apple'), (2, 'Orange'), (3, 'Banana'), (4, 'Cherry') 

DECLARE @Scores TABLE(Id INT PRIMARY KEY, Entity_Id INT, Score INT, Date_Added DATE) 
INSERT INTO @Scores 
VALUES (1,1,10,'2009-02-01'), 
(2,2,10,'2009-02-01'), 
(3,1,15,'2009-02-01'), 
(4,2,10,'2009-03-01'), 
(5,1,15,'2009-04-01'), 
(6,2,15,'2009-04-01'), 
(7,3,22,'2009-04-01') 

Bạn có thể sử dụng

SELECT E.Id, E.name, S.Score, S.Date_Added 
FROM @Entities E 
CROSS APPLY 
(
    SELECT TOP 1 * 
    FROM @Scores Sc 
    WHERE Sc.Entity_Id = E.Id 
    ORDER BY sc.Score DESC 
) AS S 

để có được những kết quả mong muốn . Các equivilent để cho phép các tổ chức mà không cần điểm số sẽ là

SELECT E.Id, E.name, S.Score, S.Date_Added 
FROM @Entities E 
OUTER APPLY 
(
    SELECT TOP 1 * 
    FROM @Scores Sc 
    WHERE Sc.Entity_Id = E.Id 
    ORDER BY sc.Score DESC 
) AS S 
1

Bạn cũng có thể làm ngày hôm nay ở hầu hết các RDBMS (Oracle, PostgreSQL, SQL Server) với một truy vấn tự nhiên sử dụng chức năng cửa sổ như ROW_NUMBER:

SELECT id, name, score, date_added FROM (
SELECT e.id, e.name, s.score, s.date_added, 
ROW_NUMBER() OVER (PARTITION BY e.id ORDER BY s.date_added DESC) rn 
FROM Entities e INNER JOIN Scores s ON e.id = s.entity_id 
) tmp WHERE rn = 1; 

SQL Fiddle

Các vấn đề liên quan