2011-07-22 17 views
19

Mam bazę danych z dwiema tabelami. Jedna z tabel zawiera użytkowników, druga zawiera adresy dla tych użytkowników. Każdy użytkownik może mieć kilka adresów (ale każdy adres jest powiązany tylko z jednym użytkownikiem).SQL - "DISTINCT" na podstawie tylko niektórych kolumn?

Chcę utworzyć wyszukiwanie, które zwraca tylko jeden wpis dla każdego użytkownika, nawet jeśli ma on kilka adresów. Nie ma znaczenia, który z adresów wyszukiwania powróci - wystarczy jedno wyszukiwanie.

Oto przykładowy wynik wyszukiwania:

tst olix Chicago IL USA 
tst olix Los Angeles CA USA 
tst2 olix2 Houston TX USA 

muszę przeszukiwanie być tak, że zwraca tylko 2 wiersze, zamiast 3.

jakieś pomysły?

SELECT DISTINCT 
    Users.Firstname, Users.Surname, Users.UserId, 
    Users.Recommendations, Addresses.City, Addresses.Region, 
    Addresses.Country 
FROM 
    Users INNER JOIN 
    Addresses ON FT_TBL.UserId = Addresses.UserId 
ORDER BY 
    Users.Recommendations 
+0

Co używasz, SQL-Server lub Access? –

+0

Jeśli był to postgresql, możesz użyć DISTINCT ON w składni. – sage88

Odpowiedz

8

Jeśli Addresses ma ID murawę:

(zaktualizowane dla SQL Server)

SELECT 
    Users.Firstname, 
    Users.Surname, 
    Users.UserId, 
    Users.Recommendations, 
    Addresses.City, 
    Addresses.Region, 
    Addresses.Country 
FROM 
    Users INNER JOIN 
    Addresses ON Users.UserId = Addresses.UserId 
WHERE Addresses.ID = 
    (SELECT TOP 1 A2.ID 
     FROM Addresses AS A2 
     WHERE Users.UserId = A2.UserId 
    ) 
ORDER BY 
    Users.Recommendations 

Korzystanie okno sql-server i rangi funkcje:

SELECT 
    Users.Firstname, 
    Users.Surname, 
    Users.UserId, 
    Users.Recommendations, 
    Addresses.City, 
    Addresses.Region, 
    Addresses.Country 
FROM 
    Users INNER JOIN 
    (SELECT * 
      , ROW_NUMBER() OVER (PARTITION BY UserID) AS rn 
     FROM Addresses 
    ) AS Addresses ON Users.UserId = Addresses.UserId 
        AND Addresses.rn = 1 
ORDER BY 
    Users.Recommendations 
+0

To wygląda na bardziej poręczne niż Oliver

+1

SQL-Server ma 'TOP' –

+0

@Oliver: A także funkcje okna, które powinny być przydatne w tym przypadku. –

7

Prawdopodobnie trzeba użyć GROUP BY zamiast DISTINCT w tym przypadku.

Opublikuj zapytanie teraz, a pomogę Ci więcej.

Alternatywnie, jeśli chcesz tylko zwrócić adres pierwszy adres, to zupełnie inne zapytanie. Czy musi zwrócić adres? Jakie dane są potrzebne? Co oznacza "pierwszy" w tym kontekście? Jak są uporządkowane dane?

Dowolnie można zrobić coś takiego (niesprawdzone), w zależności od DB:

SELECT 
    userID 
    , FIRST(address) 
FROM 
    yourTable 
GROUP BY 
    userID 
+0

Ostatecznie chcę, aby było wyszukiwanie lokalizacji, a wynik wycofany jest najbliższym miejscem do określonej lokalizacji. W tej chwili chcę tylko, aby coś cofnąć, podczas gdy pracuję nad projektem strony. – Oliver

4
SELECT Name, MAX(Address), MAX(other field)... 
FROM MyTable 
GROUP BY Name 

daje jeden wiersz za Name.

+1

+1 Twoja odpowiedź, tak jak moja, zależy od jakiegoś dowolnego porządku. PO powinien wyjaśnić. – Matthew

+3

@Matthew - mówi w pytaniu, że nie obchodzi go, który dostaje. Myślę, że mógłby się przejmować, jeśli pola są ze sobą powiązane (tj. "Adres 1, Miasto, Stan, Zip") - można uzyskać niewłaściwą kombinację stanu/adresu, która byłaby zła – JNK

+0

Dokładnie, jak: 'Los Angeles | IL | USA ". –

0

Spróbuj agregat:

SELECT user, address FROM users 
JOIN addresses ON (users.user_id = addresses.user_id) 
GROUP BY user; 
4

Zakładając, że tabela ma kolumnę adres ID:

select p.fname, p.lname, a.state, a.country 
from person p 
join address a on a.personid = p.personid 
where not exists 
    (select * 
    from address a2 
    where a2.personid = a.personid 
     and a2.addressid < a.addressid) 

Moje zapytanie zwraca wszystkich ludzi z adresami. Klauzula exists() służy do ustalenia, że ​​zwracany adres ma najniższy adresid przypisany tej osobie. Wynik będzie zawierał tylko 1 adres na osobę.


EDIT: Inny sposób to zrobić przy użyciu top, że nie zostało wykazane przez innych:

select p.fname, p.lname, a.state, a.country 
from person p 
join address a on a.addressid = 
    (select top 1 a2.addressid 
    from address a2 
    where a2.personid = p.personid) 

powinno to być bardzo skuteczny jako zapytania zagnieżdżonego będzie zwarcie na pierwszym adresem znajdując dla każdego osoba.

+0

Dzięki, znalazłem ten, który jest najłatwiejszy do zainstalowania. Wydaje się, że działa, mimo że moje identyfikatory użytkowników i adresów są alfanumeryczne, a nie tylko liczby. Czy jest jakiś problem z tą metodą powolną? Przypuszczam, że musi to być druga sekunda wyszukiwania dla każdego generowanego wyniku. – Oliver

+0

Myślę, że musisz dwa razy odwołać się do tabeli adresowej bez względu na wszystko. 'Top' wybierz, że ypercube może być rzeczywiście szybszy niż mój' exists() '. Trudno jednak stwierdzić na pewno i wielokrotnie stosowałem tę technikę, nie zauważając zbyt dużego spowolnienia. – dana

Powiązane problemy