2012-09-17 38 views
12

Tengo dos tablas, en PostgreSQL, si eso es importante, con una relación de varias. Necesito unirme a ellos para que por cada "uno" solo obtenga un resultado único de la tabla "muchos". No solo eso, sino que debo destacar los resultados específicos de la tabla "muchos".Unir uno a muchos y recuperar un solo resultado

 
TABLE_A 
ID | NAME  | DATE   | MORE COLS.... 
1 | JOHN  | 2012-01-10 | .... 
2 | LIZA  | 2012-01-10 | .... 
3 | ANNY  | 2012-01-10 | .... 
4 | JAMES  | 2012-01-10 | .... 
... 

TABLE_B 
ID | CODE1  | CODE2  | SORT 
1 | 04020  | 85003  | 1 
1 | 04030  | 85002  | 4 
2 | 81000  | 80703  | 1 
3 | 87010  | 80102  | 4 
3 | 87010  | 84701  | 5 
4 | 04810  | 85003  | 1 
4 | 04030  | 85002  | 4 
4 | 04020  | 85003  | 1 
... 

QUERY RESULT 
ID | NAME  | DATE   | CODE1  | CODE2 
1 | JOHN  | 2012-01-10 | 04020  | 85003 
2 | LIZA  | 2012-01-10 | 81000  | 80703 
3 | ANNY  | 2012-01-10 | 87010  | 80102 
4 | JAMES  | 2012-01-10 | 04810  | 85003 
... 

La columna SORT en TABLE_B es en realidad el último carácter en CODE2 reordenado. CODE2 puede terminar con 1-9, pero 3 es más importante que 5, 7, 4, 2, 1, 0, 6, 8, 9, por lo tanto 3 -> 1, 5 -> 2, 7 -> 3 y así adelante.

El problema que estoy enfrentando es que necesito la fila de TABLE_B donde ordenar es el número más bajo. En algunos casos, hay múltiples casos más bajos (vea ID = 4 en TABLE_B), entonces no importa cuál de las filas con ID más baja está seleccionada, solo que hay un único resultado para esa ID.

+1

¡Bienvenido a StackOverflow! Gracias por mostrar datos y escribir una pregunta clara. La próxima vez, sin embargo, hará la vida más fácil a aquellos que quieran ayudarlo si muestra sus datos en términos de una instrucción 'CREATE TABLE' y' INSERT' o 'COPY' para cargar los datos. De esta forma, las personas pueden probar fácilmente las respuestas de los candidatos antes de publicar, para asegurarse de que no tengan errores de sintaxis y obtengan los resultados que desea. – kgrittn

+0

Gracias por su comentario. Lo haré a partir de ahora. – thorgilsv

Respuesta

8

Más simple, más corto, más rápido con PostgreSQL de DISTINCT ON:

SELECT DISTINCT ON (a.id) 
     a.id, a.name, a.date, b.code1, b.code2 
FROM table_a a 
LEFT JOIN table_b b USING (id) 
ORDER BY a.id, b.sort 

detalles, explicaciones, de referencia y enlaces en this closely related answer.
Uso un LEFT JOIN, por lo que las filas de table_a sin ninguna fila coincidente en table_b no se eliminan.

lado notas:

mientras se deja en PostgreSQL, es prudente utilizar date como nombre de columna. Es un reserved word en cada estándar SQL y un nombre de tipo en PsotgreSQL.

También es un antipatrón para nombrar una columna de ID id. No descriptivo y no útil. Una (de muchas) posibles convenciones de nomenclatura sería nombrarla después de la tabla donde está la clave principal: table_a_id. Mismo nombre para las claves externas que hacen referencia a él (si no hay otro nombre natural que tenga precedencia).

+0

Gracias, esta solución también funciona. La denominación de las columnas fue únicamente por motivos descriptivos explícitamente para este ejemplo. – thorgilsv

+1

@thorgilsv: Entiendo. Esta versión debería ser bastante más rápida. Puede probar con ['EXPLAIN ANALYZE'] (http://www.postgresql.org/docs/current/interactive/sql-explain.html). –

5

PostgreSQL admite window function. Prueba de esto,

SELECT d.ID, d.NAME, d.DATE, d.CODE1, d.CODE2 
FROM 
(
    SELECT a.ID, a.NAME, a.DATE, 
      b.CODE1, b.CODE2, 
      ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY b.SORT ASC, b.CODE2 DESC) AS ROWNUM 
    FROM TableA a 
      INNER JOIN TableB b 
      ON a.ID = b.ID 
) d 
WHERE d.RowNum = 1 

SQLFiddle Demo

+0

Gracias, pero esto me da el mismo resultado que si me hubiese unido a todos desde TABLE_B a TABLE_A que es muchos a uno. – thorgilsv

+0

@ user1678791 ya que ha agregado la etiqueta 'postgresql', actualicé la respuesta usando la' función de ventana' –

+0

¡Eureka! Muchas gracias, esta es la solución a mi pregunta. +1 para la demostración SQLFiddle, simplemente fantástico. – thorgilsv

1

Esto es lo que yo haría en SQL Server.

SELECT a.ID, 
    a.NAME, 
    a.DATE, 
    b.CODE1, 
    b.CODE2 
FROM TABLE_A a 
JOIN TABLE_B b 
    on a.ID = b.ID 
WHERE b.SORT = (SELECT MIN(SORT) 
    FROM TABLE_B 
    WHERE ID = b.ID) 
Cuestiones relacionadas