2011-07-22 28 views
19

Tengo una base de datos con dos tablas. Una de las tablas contiene usuarios, la otra contiene direcciones para esos usuarios. Cada usuario puede tener varias direcciones (aunque cada dirección está vinculada a un solo usuario).SQL - 'DISTINCT' basado en solo algunas columnas?

Deseo crear una búsqueda que solo devuelva una entrada para cada usuario, incluso si ese usuario tiene varias direcciones. No importa a qué dirección se dirija la búsqueda: lo que encuentre primero la búsqueda es suficiente.

Aquí es un resultado de búsqueda ejemplo:

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

necesito la búsqueda de ser tal que sólo devuelve 2 filas, en lugar de 3.

alguna idea?

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

Lo usas, SQL-Server o Access? –

+0

Si esto fue postgresql, podría usar la sintaxis DISTINCT ON. – sage88

Respuesta

8

Si Addresses tiene un campo ID:

(actualizado para 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 

Uso de la ventana de SQL-Server y clasificación de funciones:

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

Eso parece más útil que la Oliver

+1

SQL-Server tiene 'TOP' –

+0

@Oliver: Y también funciones de ventana que deberían ser útiles en este caso. –

7

es probable que necesite usar GROUP BY en lugar de DISTINCT en este caso.

Publique su consulta ahora y le ayudaré más.

O bien, si solo desea devolver la primera dirección , esa es una consulta completamente diferente. ¿Necesita devolver la dirección? ¿Qué datos necesitas? ¿Qué significa "primero" en este contexto? ¿Cómo se ordenan los datos?

Arbitrariamente se podría hacer algo como esto (no probado), dependiendo de su base de datos:

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

Eventualmente quiero que sea una búsqueda de ubicación, y el resultado es el lugar más cercano a una ubicación determinada. Por el momento solo quiero que retire algo mientras trabajo en el diseño del sitio. – Oliver

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

le dará una fila por Name.

+1

+1 su respuesta, como la mía, depende de algún orden arbitrario. OP debería aclarar. – Matthew

+3

@Matthew - dice en la pregunta que no le importa cuál obtiene. Creo que le puede importar si los campos están interrelacionados (es decir, "Dirección1, ciudad, estado, código postal"); podría obtener una combinación incorrecta de estado/dirección que sería incorrecta – JNK

+0

Exactamente, como: 'Los Ángeles | IL | USA'. –

0

Pruebe un agregado:

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

Suponiendo que la tabla de direcciones tiene una columna de 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) 

Mi consulta devuelve todas las personas con las direcciones. La cláusula exists() se usa para determinar que la dirección devuelta tiene la dirección de acceso más baja asignada a la persona. El resultado solo contendrá 1 dirección por persona.


EDIT: Otra forma de hacer esto utilizando top que no ha sido demostrado por otros:

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) 

Esto debe ser muy eficiente como la consulta anidada se encuentran cortocircuito en la primera dirección para cada persona.

+0

Gracias, encontré este el más fácil de ranurar. Parece que funciona a pesar de que mis identificadores de usuario/dirección son alfanuméricos en lugar de solo números. ¿Hay algún problema con este método siendo lento? Supongo que debe estar haciendo una segunda segunda búsqueda para cada resultado que se genere. – Oliver

+0

Creo que tiene que hacer referencia a la tabla de direcciones dos veces en su consulta pase lo que pase. La selección 'superior' que tiene Ypercube puede ser más rápida que my 'exists()'. Sin embargo, es difícil de decir con certeza y he usado esta técnica muchas veces sin notar demasiado lento. – dana

Cuestiones relacionadas