2010-12-20 7 views
2

Dies ist eng verwandt mit einer vorherigen Frage, die ich gestellt habe.T-SQL - Hilfe mit MAX-Operation über Viele-zu-Viele

Ich habe eine many-to-many Beziehung zwischen Beitrag und Ort.

Die Join-Tabelle heißt PostLocations und hat nichts außer den FK's. (LocationId, PostId)

Ich versuche, die oberen Pfosten für jeden Standort zurückzuziehen.

Dies ist die Abfrage Ich habe (die in der Antwort auf meine vorherige Frage gegeben wurde):

SELECT pl.LocationId, p.postid, p.UniqueUri, p.Content, MAX(s.BaseScore) as topscore 
from dbo.PostLocations pl 
inner join dbo.posts p on pl.PostId = p.PostId 
inner join dbo.reviews r on p.postid = r.postid 
inner join dbo.scores s on r.scoreid = s.scoreid 
group by pl.locationid, p.postid, p.UniqueUri, p.Content 

Aber das Problem ist, weil PostLocations Einträge wie diese haben könnte:

LocationId PostId 
1   213213 
2   498324 
1   230943 

Also meine obige Abfrage gibt LocationId 1 zweimal zurück, weil es zwei Datensätze in der Join-Tabelle hat. Ich möchte nur 1 Datensatz pro Standort - der oberste Beitrag pro Locationid.

Ich habe auch versucht, dies:

SELECT l.LocationId, p.postid, p.UniqueUri, p.Content, MAX(s.BaseScore) as topscore 
from dbo.PostLocations pl 
inner join dbo.Locations l on pl.LocationId = l.LocationId 
inner join dbo.posts p on pl.PostId = p.PostId 
inner join dbo.reviews r on p.postid = r.postid 
inner join dbo.scores s on r.scoreid = s.scoreid 
group by l.locationid, p.postid, p.UniqueUri, p.Content 

Gleiches Ergebnis - das ist, was zurückkommt:

LocationId PostId UniqueUri Content TopScore 
1   213213 some-post pew pew 2.00 
2   498324 anot-post blah bl 4.50 
1   230943 sadjsa-as asijd a 3.5 

Dies ist, was sollte kommen zurück:

LocationId PostId UniqueUri Content TopScore 
1   230943 sadjsa-as asijd a 3.5 
2   498324 anot-post blah bl 4.50 

Da LocationId 1 über 2 Posts verfügt, hat PostId 230943 die höchste Punktzahl, sodass i Der eine ist zurückgekehrt.

Irgendwelche Ideen zu was ich vermisse?

Antwort

3

Wenn Sie SQL Server 2005 oder höher verwenden, können Sie so etwas wie tun:

With RankedLocations As 
    (
    Select PL.LocationId 
     , S.BaseScore 
     , P.PostID 
     , P.UniqueUri 
     , P.Content 
     , Row_Number() Over(Partition By PL.LocationId Order By S.BaseScore Desc) As ScoreRank 
    From dbo.PostLocations As PL 
     Join dbo.Posts As P 
      On P.PostId = PL.PostId 
     Join dbo.Reviews As R 
      On R.PostId = P.PostId 
     Join dbo.Scores As S 
      On S.ScoreId = R.ScoreId 
    ) 
Select LocationId, BaseScore, PostID, UniqueUri, Content 
From RankedLocations 
Where ScoreRank = 1 
+0

Ahh ROW_NUMBER()! Natürlich..ich benutze es überall, nur habe ich nicht gedacht, es hier zu benutzen. Funktioniert ein Charme - danke! – RPM1984

+0

+1 viel sauberer als meins, ich habe nie in Erwägung gezogen mit row_number() Zu viel Zeit mit sqlserver 2000 zu arbeiten lässt mich vergessen, dass es existiert – Robb

+0

@Robb - * GENAU * das gleiche Szenario für mich. von 5 Jahren meines Programmierlebens verwendeten 4 von ihnen SQL2000. Welt des Schmerzes. – RPM1984

1

Da Sie gruppieren auf PostID, die einzigartig ist, fällt jeder einzelne Beitrag in seine eigene Gruppe von einem.

Ich bin mir nicht sicher, ob es ein besserer Weg, dies jedoch zu tun, was ich in der Vergangenheit getan verlaufen entlang dieser Linien

Select l.LocationId, p.postid, p.UniqueUri, p.Content, s.basescore as topscore 
from 
    dbo.Locations l inner join 
    (select 
     pl.locationid, max(s.BaseScore) as topscore 
    from 
     dbo.postlocations pl 
     inner join dbo.posts p on pl.PostId = p.PostId 
     inner join dbo.reviews r on p.postid = r.postid 
     inner join dbo.scores s on r.scoreid = s.scoreid 
    group by 
     pl.locationid) as topPost on l.locationid = topPost.locationid 
    inner join dbo.postlocations pl on pl.locationid = l.locationid 
    inner join dbo.posts p on pl.PostId = p.PostId 
    inner join dbo.reviews r on p.postid = r.postid 
    inner join dbo.scores s on r.scoreid = s.scoreid and s.basescore = toppost.topscore 

Wir sind eine Unterabfrage machen die höchste Punktzahl für einen bestimmten finden Ort, dann machen Sie unsere Joins wie zuvor und stellen Sie auf dem letzten Join sicher, dass der BaseScore der Topscore ist, den wir früher gefunden haben.

Dies bedeutet, dass wenn wir zwei gleiche Top-Scores für einen bestimmten Ort haben, wir beide Zeilen zurückgeben, aber in allen anderen Fällen werden wir eine einzelne Zeile pro Standort zurückgeben, kann es einen beliebigen Post bei zwei gleich wählen Top-Ergebnisse, aber ich habe es nicht getan.

Ich bin interessiert zu sehen, ob es andere Lösungen für dieses Problem gibt als mit der Anzahl der zusätzlichen Joins ist das eine ziemlich rechenintensive Lösung für das Problem.

Bearbeiten - als Antwort auf Ihren Kommentar als PostID ist der Primärschlüssel, den wir uns darauf verlassen können, dass es für den neuesten Beitrag am besten ist.

Select l.LocationId, p.postid, p.UniqueUri, p.Content, bar.basescore as topscore 
from 
    dbo.Locations l inner join 
    (select 
     l.LocationId, max(p.postid) as postid ,max(s.basescore) as basescore 
    from 
     (select 
      pl.locationid, max(s.BaseScore) as topscore 
     from 
      dbo.postlocations pl 
      inner join dbo.posts p on pl.PostId = p.PostId 
      inner join dbo.reviews r on p.postid = r.postid 
      inner join dbo.scores s on r.scoreid = s.scoreid 

     group by 
      pl.locationid) as topPost on l.locationid = topPost.locationid 
     inner join dbo.postlocations pl on pl.locationid = l.locationid 
     inner join dbo.posts p on pl.PostId = p.PostId 
     inner join dbo.reviews r on p.postid = r.postid 
     inner join dbo.scores s on r.scoreid = s.scoreid and s.basescore = toppost.topscore 
    group by l.locationid) as bar on l.locationid = bar.locationid 
    inner join posts p on bar.postid = p.postid 
+0

Ja, ich postid Gruppierung dachte das Problem war. Ja, das funktioniert, aber gibt Duples zurück, wie du sagst, wenn das Ergebnis gleich ist. Irgendwelche Ideen, wie ich ändern kann, um zu sagen, wenn es Betrüger gibt, schnappen Sie das "späteste" getan (datemodified desc)? – RPM1984

+0

@Thomas Antwort ist besser - aber +1 für Sie für die Anstrengung, und bietet eine praktikable Lösung. Vielen Dank. – RPM1984

+0

@ RMP1984 - Ta, Code hinzugefügt, um den neuesten Beitrag mit der Bestnote aus Gründen der Vollständigkeit zu greifen – Robb