2009-03-04 6 views
2

Ich habe zwei Tabellen:.. TableA (ID [int, pk], Name [string]) und TableB(ID [int, pk], TableA_ID [int, fk], Name [string], DateStamp [datetime (dd/mm/yyyy hh:MM:ss)]) Es gibt eine Beziehung zwischen TableA und TableBist dies ein Kandidat für eine sql groupby Abfrage?

one-to-many ist

eine innere Verknüpfung auf den beiden Tabellen geben mir die folgenden Ergebnisse:

 
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 

Was ich wirklich will, ist die folgende (jede Zeile von TableA und die letzten passenden Zeile von 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 

Dies ist die Abfrage, die ich verwende, dies zu erreichen:

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) 

Das aber Gefühl ist mein gut funktioniert, dass ich tue dies nicht in dem „besten Weg“. Es sieht so aus, als wäre es ein Kandidat für eine Sammelanfrage (d. H. Groupby), aber ich hatte damit kein Glück. Am Ende musste ich immer eine Unterabfrage verwenden, um die Zeile zu erhalten, nach der ich in TableB gesucht habe.

Jede Hilfe sehr geschätzt.

Antwort

5

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 
    ) 

ein zusätzlicher GROUP BY ist nur erforderlich, wenn y: Nein, es nicht erforderlich ist GROUP BY zu tun hier, soll dies durch eine korrelierte Unterabfrage gelöst werden Sie haben mehr als einen Datensatz in TableB mit gleichen TableA_IDund gleich DateStamp.


Für das spezielle Beispiel, das Sie gezeigt haben, eine GROUP BY-Abfrage geschieht das richtige Ergebnis zu produzieren. Es ist immer noch falsch, weil das richtige Ergebnis in dieser Situation eher ein Nebeneffekt ist.

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 

Dies beruht auf dem Zufall, dass MAX(TableB.Name) in der Tat ist der Wert, den Sie aussteigen wollen, und es ist mit MAX(TableB.DateStamp) ausgerichtet ist. Da diese Korrelation jedoch nur ein Zufall ist, ist die GROUP BY-Abfrage falsch.

+0

ich auf der obersten Ebene Gruppe in dieser Situation durch den Einsatz war auf der Suche, und kam zu ähnlichen Schlussfolgerungen wie Ihre. – kristof

+0

Ich würde vorschlagen, TableB.TableB_ID in der ersten Abfrage zu verwenden, wenn möglich (d. H. Wenn es die gleiche Reihenfolge wie DateStamp hat), da der Ganzzahlvergleich immer schneller ist als der Datumsvergleich. –

+0

Ja, aber ich kann nicht stillschweigend eine TableB.TableB_ID implizieren, die streng monoton zunimmt. – Tomalak

0

wenn Sie wollen Gruppe verwenden, indem Sie verwenden können:

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 

Aber ich werde mehrere Datensätze zurück, wenn Sie mehrere Einträge in tableB mit dem gleichen Wert von Date Referenzierung derselben Zeile in tableA haben

0

Sie können nicht zuverlässig erhalten mehr als ein Feld aus der B-Tabelle in einer Gruppierung, aber Sie können die B-Tabelle vor dem Ergebnis kommen Sie mit den anderen Bereichen zu erhalten:

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

Sie können auch eine machen Abfrage mit analytischen Funktionen.In Oracle können Sie tun:

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

Sie auch RANK() OVER-Funktion versuchen:

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

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

+0

Ihre Antwort wäre * viel * klarer, wenn Sie zwischen den Dateneinfügungen und der tatsächlichen Select-Anweisung unterscheiden. Ich würde die Beilagen tatsächlich entfernen, wenn ich Sie wäre, weil sie nicht wirklich Teil der Antwort sind. –

+0

Ich dachte, es ist einfach, die ganze Antwort mit Testdaten auf einmal zu kopieren ... aber ich stehe meinen Kunden gegenüber :) und volia - Kommentare zeigen dir den Weg. –

+0

Danke. Es ist viel klarer auf diese Weise. Ich würde vorschlagen, dass Sie die tatsächliche Antwort an die Spitze verschieben, weil die meisten Leute daran interessiert sind, anstatt in Testdaten. –

Verwandte Themen