2009-03-04 5 views
2

J'ai deux tables:. tableA (ID [int, pk], Name [string]) et TableB(ID [int, pk], TableA_ID [int, fk], Name [string], DateStamp [datetime (dd/mm/yyyy hh:MM:ss)]) Il y a un à plusieurs entre TableA et TableBest-ce un candidat pour une requête sql groupby?

Une jointure interne sur les deux tableaux me donner les résultats suivants.:

 
TableA.ID, TableA.Name, TableB.Name, TableB.DateStamp 
     1, 'File A', 'Version 1', 01/01/2009 15:00:00 
     1, 'File A', 'Version 2', 05/01/2009 08:15:00 
     1, 'File A', 'Version 3', 06/01/2009 19:33:00 
     2, 'File B', 'Version 1', 03/01/2009 09:10:00 
     2, 'File B', 'Version 2', 20/01/2009 20:00:00 
     3, 'File C', 'Version 1', 01/01/2009 17:00:00 

Ce que je veux en fait est la suivante (chaque ligne de TableA et la dernière ligne correspondante de TableB):

 
TableA.ID, TableA.Name, TableB.Name, TableB.DateStamp 
     1, 'File A', 'Version 3', 06/01/2009 19:33:00 
     2, 'File B', 'Version 2', 20/01/2009 20:00:00 
     3, 'File C', 'Version 1', 01/01/2009 17:00:00 

Ceci est la requête que j'utilise pour y parvenir:

SELECT ta.ID, ta.Name, tb.Name, tb.DateStamp 
FROM TableA ta INNER JOIN TableB tb ON ta.ID = tb.TableA_ID 
WHERE tb.ID IN (
SELECT TOP 1 tb2.ID 
FROM TableB tb2 
WHERE tb2.TableA_ID = ta.ID 
ORDER BY tb2.DateStamp DESC) 

Cela fonctionne, mais mon sentiment est que je ne fais pas ça dans la « meilleure façon ». Il semble que ce soit un candidat pour une requête d'agrégation (c'est-à-dire groupby) mais je n'ai pas eu de chance avec cela. À la fin j'ai toujours dû utiliser une sous-requête pour obtenir la rangée que je suis après dans la TableB.

Toute aide très appréciée.

Répondre

5

Non, il n'y a pas d'obligation de le faire GROUP BY ici, cela devrait être résolu par une sous-requête corrélative:

SELECT 
    TableA.ID, 
    TableA.Name, 
    TableB.Name, 
    TableB.DateStamp 
FROM 
    TableA 
    INNER JOIN TableB ON 
    TableA.ID = TableB.TableA_ID 
    AND TableB.DateStamp = (
     SELECT MAX(DateStamp) 
     FROM TableB 
     WHERE TableA_ID = TableA.ID 
    ) 

un autre groupe BY est nécessaire que si y vous avez plus d'un enregistrement dans TableB avec TableA_IDet égal DateStamp.


Pour l'exemple spécifique que vous avez montré, une requête GROUP BY arrive à produire le résultat correct. C'est toujours faux, parce que le résultat correct est plus un effet secondaire dans cette situation.

SELECT 
    TableA.ID, 
    TableA.Name, 
    MAX(TableB.Name) Max_TableBName, 
    MAX(TableB.DateStamp) Max_TableBDateStamp 
FROM 
    TableA 
    INNER JOIN TableB ON TableA.ID = TableB.TableA_ID 
GROUP BY 
    TableA.ID, 
    TableA.Name 

Cela repose sur la coïncidence que MAX(TableB.Name) est en fait la valeur que vous voulez sortir, et il est aligné avec MAX(TableB.DateStamp). Mais puisque cette corrélation est un simple accident, la requête GROUP BY est erronée.

+0

Je regardais le groupe de haut niveau par l'utilisation dans cette situation, et est arrivé à des conclusions similaires à la vôtre. – kristof

+0

Je suggérerais d'utiliser TableB.TableB_ID dans la première requête si possible (c'est-à-dire s'il a le même ordre que DateStamp), car la comparaison d'entier est toujours plus rapide que la comparaison de date. –

+0

Oui, mais je ne peux pas insinuer silencieusement un TableB.TableB_ID qui est strictement monotone. – Tomalak

0

si vous souhaitez utiliser le groupe par vous pouvez utiliser:

select 
    ta.id, ta.name, tb.name, tb.dateStamp 
from 
    tableA ta 
    inner join tableB tb on ta.id = tb.tablea_id 
    inner join (
     select tablea_id, max(DateStamp) as maxDateStamp from tableB 
     group by tablea_id 
    ) latestB 
     on tb.tablea_id = latestB.tablea_id 
     and tb.DateStamp = latestB.maxDateStamp 

Mais je retournerez plusieurs enregistrements si vous avez plusieurs entrées dans tableB avec la même valeur de DateStamp référence même ligne dans tableA

0

Vous ne pouvez pas fiable obtenir plus d'un champ de la table B dans un groupe, mais vous pouvez vous joindre à la table B contre le résultat pour obtenir les autres champs:

select x.ID, x.Name, b.Name, b.DateStamp 
from (
    select a.ID, a.Name, max(b.DateStamp) as DateStamp 
    from TableA a 
    inner join TableB b on b.TableA_ID = a.ID 
    group by a.ID, a.Name 
) x 
inner join TableB b on b.TableA_ID = x.ID and b.DateStamp = x.DateStamp 
1

Vous pouvez également faire un interroger en utilisant des fonctions analytiques.Dans Oracle, vous pouvez faire:

select distinct 
     A.Id 
,  A.Name 
,  first_value(B.Name)  over (partition by B.id 
             order  by B.DateStamp desc) BName 
,  first_value(B.DateStamp) over (partition by B.id 
             order  by B.DateStamp desc) DateStamp 
from TableA A inner join TableB B on A.id = B.id 
3

Vous pouvez également essayer RANK() OVER fonction:

-- Test data 
DECLARE @TableA TABLE (ID INT, Name VARCHAR(20)) 
INSERT INTO @TableA 
SELECT 1, 'File A' UNION 
SELECT 2, 'File B' UNION 
SELECT 3, 'File C' 

DECLARE @TableB TABLE (ID INT, TableAID INT, Name VARCHAR(20), 
    DateStamp DATETIME) 
INSERT INTO @TableB 
SELECT 1, 1, 'Version 1', '01/01/2009 15:00:00' UNION 
SELECT 2, 1, 'Version 2', '01/05/2009 08:15:00' UNION 
SELECT 3, 1, 'Version 3', '01/06/2009 19:33:00' UNION 
SELECT 4, 2, 'Version 1', '01/03/2009 09:10:00' UNION 
SELECT 5, 2, 'Version 2', '01/20/2009 20:00:00' UNION 
SELECT 6, 3, 'Version 1', '01/01/2009 17:00:00' 

-- Actually answer 
SELECT M.ID, M.AName, M.BName, M.DateStamp FROM 
( SELECT RANK() OVER(PARTITION BY A.ID ORDER BY B.DateStamp DESC) AS N, 
    A.ID, A.Name AS AName, B.Name AS BName, B.DateStamp 
    FROM @TableA A INNER JOIN @TableB B ON A.ID = B.TableAID 
) M WHERE M.N = 1 

Voir 2. Last Date selection with grouping - using RANK() OVER

+0

Votre réponse serait * beaucoup * plus claire si vous faites la différence entre les insertions de données et l'instruction select réelle. Je supprimerais les inserts si j'étais vous, car ils ne font pas partie de la réponse. –

+0

J'ai pensé qu'il est facile de copier toute la réponse avec des données de test à la fois ... mais je suis steppin 'envers mes clients :) et volia - les commentaires vous montrent le chemin. –

+0

Merci. C'est beaucoup plus clair de cette façon. Je proposerais que vous déplaciez la réponse réelle au sommet, parce que la plupart des gens s'y intéresseront, plutôt que dans des données de test. –

Questions connexes