web-dev-qa-db-ja.com

あるテーブルの最新の行を別のテーブルに結合するにはどうすればよいですか?

次のようなデータがあります。

entities
id         name
1          Apple
2          Orange
3          Banana

定期的にプロセスが実行され、各エンティティにスコアが付けられます。プロセスはデータを生成し、次のようにスコアテーブルに追加します。

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

すべてのエンティティと、それぞれの最新の記録スコアを選択できるようにしたいので、次のようなデータが得られます。

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

このクエリを使用して、単一のエンティティのデータを取得できます。

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

しかし、すべてのエンティティに同じものを選択する方法に困惑しています。おそらくそれは私を顔で見つめているのでしょうか?

ご清聴ありがとうございました。

素晴らしい反応をありがとう。希望する解決策が浮上するかどうかを確認するために数日与えてから、答えを選択します。

更新:提案されたソリューションのいくつかを試してみましたが、現在直面している主な問題は、エンティティがまだ生成されたスコアを持っていない場合、リストに表示されないことです。

まだスコアが投稿されていない場合でも、すべてのエンティティが確実に返されるようにするには、SQLはどのようになりますか?

更新:回答が選択されました。みんな、ありがとう!

50
GloryFish

私はこのようにします:

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;
63
Bill Karwin

私のバリエーションを追加するだけです:

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
)
9
Ray Hidayat

アプローチ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)
5
Michael Buen

アプローチ2

バッチに対するクエリコスト:


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
3
Michael Buen

これは古い質問であり、誰もまだ言及していないアプローチを追加すると思いました。Cross ApplyまたはOuter Apply。これらはSQL Server 2005で利用可能です(この質問ではデータベースタイプはタグ付けされていません)またはそれ以上

一時テーブルを使用する

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')

使用できます

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

望ましい結果を得るために。スコアのないエンティティを許可する同等の権限は

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
3
Manatherin
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
1
Otávio Décio

ROW_NUMBERなどのウィンドウ関数を使用した自然なクエリを使用して、ほとんどのRDBMS(Oracle、PostgreSQL、SQL Server)で今日これを行うこともできます。

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

1
Cristi S.