SINISTRA ISCRIVITI solo alla prima riga


138

Ho letto molte discussioni su come ottenere solo la prima riga di un join sinistro, ma, per qualche motivo, questo non funziona per me.

Ecco la mia struttura (semplificata ovviamente)

Feed

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

artisti

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

feeds_artists

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

Ora voglio ottenere gli articoli e unirmi solo al primo artista e ho pensato a qualcosa del genere:

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 per ottenere solo la prima riga dei feeds_artists, ma già questo non funziona.

Non riesco a utilizzare a TOPcausa del mio database e non riesco a raggruppare i risultati in feeds_artists.artist_idquanto ho bisogno di ordinarli per data (ho ottenuto i risultati raggruppandoli in questo modo, ma i risultati non erano i più recenti)

Ho provato qualcosa anche con OUTER APPLY - nessun successo. Ad essere sincero, non riesco davvero a immaginare cosa stia succedendo in quelle file - probabilmente il motivo principale per cui non riesco a farlo funzionare.

SOLUZIONE:

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'



Dovresti mettere la soluzione come una risposta in modo che possiamo votare e possa essere più visibile per i futuri visitatori
fguillen,

Immagino che tu abbia ragione: l'ho anche aggiunto come risposta.
KddC,

Risposte:


97

Se puoi presumere che gli ID artista aumentino nel tempo, allora MIN(artist_id)sarà il primo.

Quindi prova qualcosa del genere (non testato ...)

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

Grazie per la tua rapida risposta. Questa non era la risposta esatta, ma mi ha portato sulla strada giusta. Ho sempre cercato di unirmi ad entrambi allo stesso livello invece di far dipendere l'uno dall'altro. Grazie mille per avermi condotto sulla strada giusta. Edito il primo post
KddC il

4
A questo punto una semplice query secondaria non sarebbe migliore? Perché ora hai un join e una query secondaria. Sto solo chiedendo perché sto cercando una soluzione allo stesso problema :)
galdikas,

2
la subquery è troppo lenta.
Sinux,

1
@Sinux definisce "troppo lento". Dipende dal numero di record e da determinati requisiti.
osservatore

1
Questo non funzionerà! La query secondaria NON consente il passaggio del campo dalla query principale !!!
Thư Sinh

53

Versione senza sottoselezione:

   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

2
non penso che questa sarà la prima riga (primo ID o prima di qualcos'altro), è solo una scelta casuale tra le righe di join a sinistra.
Sinux,

26
Questa query è errata. Seleziona il nome dell'artista "più basso", non il nome dei primi artisti nel set.
Glapa,

5
Sbagliato per la domanda ... ma esattamente quello che voglio. Nel mio caso, voglio solo il primo ID dal tavolo a cui mi sto unendo.
Drew Stephens

2
Il problema qui è che se decidi di fare un COUNT o SUM avrai rovinato i dati. Il problema con Subselect è che è più pesante per ottenere i dati in quanto ha creato una tabella temporanea ... Vorrei che MySql potesse avere un LIMIT a livello di LEFT JOIN.
Shadoweb,

Questa soluzione presenta problemi di prestazioni. Utilizzare invece la soluzione Min / Max.
Sadegh PM,

25

La risposta di @Matt Dodges mi ha messo sulla strada giusta. Grazie ancora per tutte le risposte, che nel frattempo hanno aiutato molti ragazzi. Ha funzionato così:

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'

Questa è una risposta che funziona solo per la prima fila. Si rompe quando non c'è f.idcondizione.
Tajni,

2

Ho usato qualcos'altro (penso di meglio ...) e voglio condividerlo:

Ho creato una VISTA con una clausola "gruppo"

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

Voglio ancora dire che penso che dobbiamo fare questa soluzione PERCHÉ ABBIAMO FATTO QUALCOSA DI SBAGLIATO NELL'ANALISI ... almeno nel mio caso ... ma a volte è più economico farlo per ridisegnare tutto ...

Spero possa essere d'aiuto!


Supponendo che ci siano più file (province?) Per ciascuna country_code, ciò GROUP BYè improprio. Vedere ONLY_FULL_GROUP_BY.
Rick James,

Non è necessario creare una vista da utilizzare esclusivamente per LEFT / INNER JOIN ?! È possibile utilizzare una subquery o una WITH x AS (clausola?
Kiradotee,

2

sulla base di diverse risposte qui, ho trovato qualcosa che ha funzionato per me e volevo generalizzare e spiegare cosa sta succedendo.

convertire:

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

per:

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

la condizione che collega t1 e t2 viene spostata dalla ONe nella query interna WHERE. la MIN(primary key)o LIMIT 1fa in modo che solo 1 riga viene restituita dalla query interna.

dopo aver selezionato una riga specifica dobbiamo dire ONquale riga è. ecco perché ONsta confrontando la chiave primaria della tabella unita.

puoi giocare con la query interna (es. ordine + limite) ma deve restituire una chiave primaria della riga desiderata che indicherà ONla riga esatta da unire.


note: funzionerà anche solo LIMITo solo MIN. la ONcondizione deve essere sulla chiave primaria della tabella unita per evitare l'impatto sulle prestazioni.
Oriadam,

1

Voglio dare una risposta più generalizzata . Uno che gestirà qualsiasi caso quando si desidera selezionare solo il primo elemento in un JOIN SINISTRA .

Puoi usare una sottoquery che GROUP_CONCATS sia quella che vuoi (anche ordinata!), Quindi dividere il risultato GROUP_CONCAT e prendere solo il suo primo elemento, in questo modo ...

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

Poiché abbiamo DESC come opzione ORDER BY , questo restituirà un ID persona per qualcuno come "Zack". Se volessimo qualcuno con il nome "Andy", cambieremmo ORDER BY FirstName DESC in ORDER BY FirstName ASC .

Questo è agile, in quanto mette il potere di ordinare totalmente nelle tue mani. Ma, dopo molti test, non si ridimensionerà bene in una situazione con molti utenti e molti dati.

È tuttavia utile nell'esecuzione di report ad uso intensivo di dati per l'amministratore.


Il GROUP_CONCATtrucco è valido fintanto che il numero di valori è limitato. Il limite predefinito è 1024 caratteri.
Rick James,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.