IZQUIERDA ÚNETE solo en la primera fila

138

Leí muchos hilos sobre obtener solo la primera fila de una unión izquierda, pero, por alguna razón, esto no funciona para mí.

Aquí está mi estructura (simplificada, por supuesto)

Feeds

id |  title | content
----------------------
1  | Feed 1 | ...

Artistas

artist_id | artist_name
-----------------------
1         | Artist 1
2         | Artist 2

feeds_artists

rel_id | artist_id | feed_id
----------------------------
1      |     1     |    1 
2      |     2     |    1 
...

Ahora quiero obtener los artículos y unirme solo al primer Artista y pensé en algo como esto:

SELECT *
    FROM feeds 
    LEFT JOIN feeds_artists ON wp_feeds.id = (
        SELECT feeds_artists.feed_id FROM feeds_artists
        WHERE feeds_artists.feed_id = feeds.id 
    LIMIT 1
    )
WHERE feeds.id = '13815'

solo para obtener solo la primera fila de feeds_artists, pero esto ya no funciona.

No puedo usarlo TOPdebido a mi base de datos y no puedo agrupar los resultados feeds_artists.artist_idporque necesito ordenarlos por fecha (obtuve los resultados al agruparlos de esta manera, pero los resultados no fueron los más nuevos)

Intenté algo con OUTER APPLY también, sin éxito también. Para ser honesto, realmente no puedo imaginar lo que está sucediendo en esas filas, probablemente la razón más importante por la que no puedo hacer que esto funcione.

SOLUCIÓN:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'
KddC
fuente
aquí está la solución stackoverflow.com/a/7588442/612987
Wasim A.
Debería poner la solución como respuesta para que podamos votarla y que sea más visible para los futuros visitantes
fguillen
Supongo que tienes razón, también lo agregué como respuesta.
KddC

Respuestas:

97

Si puede suponer que las ID de los artistas aumentan con el tiempo, entonces MIN(artist_id)será la más temprana.

Así que intente algo como esto (no probado ...)

SELECT *
  FROM feeds f
  LEFT JOIN artists a ON a.artist_id = (
    SELECT
      MIN(fa.artist_id) a_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.feed_id
  ) a
Matt Dodge
fuente
Gracias por su rápida respuesta. Esta no fue la respuesta exacta, pero me puso totalmente en el camino correcto. Siempre traté de unir ambos en el mismo nivel en lugar de hacer que uno dependiera del otro. Muchas gracias por guiarme en el camino correcto. Editado el primer post
KddC
44
¿No sería mejor en este momento una simple subconsulta? Porque ahora tienes una combinación y una subconsulta. Solo pregunto porque estoy buscando una solución al mismo problema :)
galdikas
2
La subconsulta es demasiado lenta.
Sinux
1
@Sinux define "demasiado lento". Depende del número de registros y de los requisitos dados.
observador
1
¡Esto no funcionará! ¡La subconsulta NO permite pasar el campo de la consulta principal!
Jue Sinh
53

Versión sin subselección:

   SELECT f.title,
          f.content,
          MIN(a.artist_name) artist_name
     FROM feeds f
LEFT JOIN feeds_artists fa ON fa.feed_id = f.id
LEFT JOIN artists a ON fa.artist_id = a.artist_id
 GROUP BY f.id
Denis Khvorostin
fuente
2
No creo que esta sea la primera fila (primera identificación o la primera de otra cosa), solo elige aleatoriamente una de las filas de unión izquierdas.
Sinux
26
Esta consulta es incorrecta. Seleccionará el nombre del artista "más bajo", no el nombre de los primeros artistas del conjunto.
Glapa
55
Incorrecto para la pregunta ... pero exactamente lo que quiero. En mi caso, solo quiero la primera identificación de la mesa a la que me estoy uniendo.
Drew Stephens
2
El problema aquí es que si decides hacer un RECUENTO o una SUMA, habrás arruinado los datos. El problema con Subselect es que es más pesado para obtener los datos ya que creó una tabla temporal ... Desearía que MySql pudiera tener un LÍMITE en el nivel de IZQUIERDA UNIRSE.
Shadoweb
Esta solución tiene un problema de rendimiento. Use la solución Min / Max en su lugar.
Sadegh PM
25

La respuesta de @Matt Dodges me puso en el camino correcto. Gracias de nuevo por todas las respuestas, que ayudaron a muchos chicos mientras tanto. Lo tengo funcionando así:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'
KddC
fuente
Esta es la respuesta que funciona solo para la primera fila. Se rompe cuando no hay f.idcondición.
Tajni
2

He usado algo más (creo que mejor ...) y quiero compartirlo:

Creé una vista que tiene una cláusula de "grupo"

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

Quiero decir todavía, que creo que necesitamos hacer esta solución PORQUE HICIMOS ALGO MAL EN EL ANÁLISIS ... al menos en mi caso ... pero a veces es más barato hacer esto que rediseñar todo ...

¡Espero que ayude!

Ari Waisberg
fuente
Suponiendo que haya varias filas (¿provincias?) Para cada una country_code, eso GROUP BYes incorrecto. Ver ONLY_FULL_GROUP_BY.
Rick James
¡No tiene que crear una vista para ser utilizada únicamente para la UNIÓN IZQUIERDA / INTERNA ?! ¿Se puede usar una subconsulta o una WITH x AS (cláusula?
kiradotee
2

basado en varias respuestas aquí, encontré algo que funcionó para mí y quería generalizar y explicar lo que está sucediendo.

convertir:

LEFT JOIN table2 t2 ON (t2.thing = t1.thing)

a:

LEFT JOIN table2 t2 ON (t2.p_key = (SELECT MIN(t2_.p_key) 
    FROM table2 t2_ WHERE (t2_.thing = t1.thing) LIMIT 1))

la condición que conecta t1 y t2 se mueve desde ONy hacia la consulta interna WHERE. la MIN(primary key)o LIMIT 1se asegura de que sólo el 1 fila es devuelta por la consulta interna.

después de seleccionar una fila específica, necesitamos decir ONqué fila es. Es por eso que ONestá comparando la clave principal de la tabla unida.

puede jugar con la consulta interna (es decir, orden + límite), pero debe devolver una clave principal de la fila deseada que le dirá a la ONfila exacta a unirse.

oriadam
fuente
notas: también funcionará con solo LIMITo solo MIN. la ONcondición debe estar en la clave primaria de la tabla unida para evitar el impacto en el rendimiento.
Oriadam
1

Quiero dar una respuesta más generalizada . Uno que manejará cualquier caso cuando desee seleccionar solo el primer elemento de una IZQUIERDA .

Puede usar una subconsulta que GROUP_CONCATS lo desee (¡también ordenada!), Luego simplemente divida el resultado de GROUP_CONCAT'd y tome solo su primer elemento, así ...

LEFT JOIN Person ON Person.id = (
    SELECT SUBSTRING_INDEX(
        GROUP_CONCAT(FirstName ORDER BY FirstName DESC SEPARATOR "_" ), '_', 1)
    ) FROM Person
);

Como tenemos DESC como nuestra opción ORDER BY , esto devolverá una identificación de persona para alguien como "Zack". Si quisiéramos a alguien con el nombre "Andy", cambiaríamos ORDER BY FirstName DESC a ORDER BY FirstName ASC .

Esto es ágil, ya que esto pone el poder de ordenar totalmente en tus manos. Pero, después de muchas pruebas, no escalará bien en una situación con muchos usuarios y muchos datos.

Sin embargo, es útil para ejecutar informes intensivos en datos para el administrador.

HoldOffHunger
fuente
El GROUP_CONCATtruco es bueno siempre que el número de valores sea limitado. El límite predeterminado es 1024 caracteres.
Rick James