2010-06-03 12 views
5

Mam 2 tabele, które wyglądają jak:Jak SELECT DISTINCT informacji z Top 1 Info i zamówienia przez od szczytu 1 Info

CustomerInfo(CustomterID, CustomerName) 
CustomerReviews(ReviewID, CustomerID, Review, Score) 

Chcę poszukać opinii na sznurku i powrócić CustomerInfo.CustomerID i CustomerInfo.CustomerName . Jednak chcę tylko pokazać wyraźny CustomerID i CustomerName wraz z tylko jednym z ich CustomerReviews.Reviews i CustomerReviews.Score. Chcę również zamówić przez CustomerReviews.Score.

nie mogę dowiedzieć się, jak to zrobić, ponieważ klient może pozostawiać wiele opinii, ale chcę tylko listę klientów z ich najwyżej ocenionego recenzji.

Wszelkie pomysły?

+0

Więc chcesz wyszukać opinie na sznurku, ale przywrócić że najwyższa punktacja weryfikacja klienta, nawet jeśli sama nie pasuje ciąg? –

+0

Czy istnieje możliwość, że klient może mieć więcej niż jedną recenzję z tą samą najwyższą wartością wyniku? A jeśli tak, czy chcesz ich wszystkich, czy tylko jednego? A jeśli tylko jeden z nich, który? („Nie obchodzi mnie to” nie jest prawidłowym rozwiązaniem.) –

+0

Tylko jeden z nich, ale chciałbym, że ocena dopasować ciąg. –

Odpowiedz

5

zrobić to największa-n-per-grupy problem, który ma pochodzić dziesiątki razy na przepełnienie stosu.

Oto rozwiązanie, które współpracuje z funkcją okna:

WITH CustomerCTE (
    SELECT i.*, r.*, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Score DESC) AS RN 
    FROM CustomerInfo i 
    INNER JOIN CustomerReviews r ON i.CustomerID = r.CustomerID 
    WHERE CONTAINS(r.Review, '"search"') 
) 
SELECT * FROM CustomerCTE WHERE RN = 1 
ORDER BY Score; 

A oto rozwiązanie, które działa szerzej z markami RDBMS, które nie obsługują funkcji okna:

SELECT i.*, r1.* 
FROM CustomerInfo i 
INNER JOIN CustomerReviews r1 ON i.CustomerID = r1.CustomerID 
    AND CONTAINS(r1.Review, '"search"') 
LEFT OUTER JOIN CustomerReviews r2 ON i.CustomerID = r2.CustomerID 
    AND CONTAINS(r1.Review, '"search"') 
    AND (r1.Score < r2.Score OR r1.Score = r2.Score AND r1.ReviewID < r2.ReviewID) 
WHERE r2.CustomerID IS NULL 
ORDER BY Score; 

jestem przedstawiający funkcję CONTAINS() dlatego należy używać funkcji wyszukiwania pełny SQL Server, nie używając LIKE ze znakami.

+0

Dzięki. Mam włączone pełne wyszukiwanie tekstu. –

+0

Dziękuję bardzo! To ma dużo więcej sensu i wydaje się, że działa dobrze pod względem wydajności. Mam włączony pełny tekst, a "wynik", o którym mówiłem, jest w rzeczywistości "stopniem" od wyszukiwania pełnotekstowego. Nie byłem pewien, czy wiele osób zna pełny tekst. Myślę, że mogę zastąpić "wynik" słowem "rank" i dołączyć do CONTAINSTABLE. –

+0

Awesome! Miło, że mogłem pomóc. –

0

myślę, że to powinno wystarczyć

select ci.CustomterID, ci.CustomerName, cr.Review, cr.Score 
from CustomerInfo ci inner join 
(select top 1* 
from CustomerReviews 
where Review like '%search%' 
order by Score desc) cr on ci.CustomterID = cr.CustomterID 
order by cr.Score 
+0

Czy istnieje szybsze zapytanie? Wydaje się, że byłoby to bardzo powolne z wieloma rekordami, ponieważ przeprowadzę indywidualne wyszukiwanie dla każdego rekordu klienta. Dzięki! –

+0

Nie wiem w żaden inny sposób, czy sprawdziłeś plan wykonania kwerendy? – alejandrobog

1

Głosowałem za odpowiedź Bill Karwin, ale pomyślałem, że wyrzucić inną opcję.

Używa on opcji correlated subquery, która często może powodować problemy z wydajnością w przypadku dużych zestawów danych, więc należy zachować ostrożność. Myślę, że jedyną zaletą jest to, że zapytanie jest łatwiejsze do natychmiastowego zrozumienia.

select * 
from [CustomerReviews] r 
where [ReviewID] = 
(
    select top 1 [ReviewID] 
    from [CustomerReviews] rInner 
    where rInner.CustomerID = r.CustomerID 
    order by Score desc 
) 
order by Score desc 

Nie dodałem filtra wyszukiwania ciągów, ale można go łatwo dodać.

+0

Tak, ale staram się unikać skorelowanych podzapytań, jeśli istnieje inne rozwiązanie, które nie jest skorelowane.Drugie rozwiązanie jest * zwykle * bardziej wydajne (choć jest wyjątek od każdej reguły). –