ESQUERDA JOIN apenas primeira linha

138

Eu li muitos tópicos sobre como obter apenas a primeira linha de uma junção esquerda, mas, por algum motivo, isso não funciona para mim.

Aqui está minha estrutura (simplificada, é claro)

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 
...

Agora eu quero pegar os artigos e juntar-me apenas ao primeiro Artist e pensei em algo assim:

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'

apenas para obter apenas a primeira linha dos feeds_artists, mas isso já não funciona.

Não posso usar TOPpor causa do meu banco de dados e não consigo agrupar os resultados, feeds_artists.artist_idpois preciso classificá-los por data (obtive resultados agrupando-os dessa maneira, mas os resultados não são os mais recentes)

Tentei algo com OUTER APPLY também - sem sucesso também. Para ser honesto, não consigo imaginar o que está acontecendo nessas linhas - provavelmente a maior razão pela qual não consigo fazer isso funcionar.

SOLUÇÃO:

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
fonte
aqui é solução stackoverflow.com/a/7588442/612987
Wasim A.
Você deve colocar a solução como uma resposta para que possamos votar-lo e ele pode ser mais visível para os visitantes futuros
fguillen
Eu acho que você está certo - eu também adicionei como resposta.
KddC 17/01/19

Respostas:

97

Se você puder supor que os IDs de artistas aumentam com o tempo, MIN(artist_id)será o mais antigo.

Então, tente algo assim (não testado ...)

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
fonte
Obrigado pela sua resposta rápida. Esta não foi a resposta exata, mas me colocou totalmente no caminho certo. Eu sempre tentei juntar os dois no mesmo nível, em vez de fazer um depender do outro. Muito obrigado por me levar no caminho certo.
Editou
4
Nesse ponto, uma subconsulta simples não seria melhor? Porque agora você tem uma associação e uma subconsulta. Só estou perguntando porque eu estou procurando a solução para o mesmo problema :) #
2800
2
subconsulta é muito lenta.
Sinux
1
@Sinux define "muito lento". Depende do número de registros e de determinados requisitos.
observador
1
Isso não vai funcionar! A subconsulta NÃO permite passar o campo da consulta pai !!!
Thư Sinh 10/03
53

Versão sem subseleção:

   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
fonte
2
Eu não acho que essa será a primeira linha (primeiro id ou primeiro de outra coisa), é apenas aleatoriamente escolher um entre as linhas de junção esquerda.
Sinux
26
Esta consulta está errada. Ele selecionará o nome do artista "mais baixo" e não o nome dos primeiros artistas do conjunto.
Glapa 12/08/16
5
Errado para a pergunta ... mas exatamente o que eu quero. No meu caso, eu só quero o primeiro ID da mesa eu estou juntando-se.
de Drew Stephens
2
O problema aqui é que, se você decidir fazer uma COUNT ou SUM, terá estragado os dados. O problema com o Subselect é que é mais pesado para obter os dados, pois ele criou uma tabela temporária ... Eu gostaria que o MySql pudesse ter um LIMIT no nível LEFT JOIN.
Shadoweb
Esta solução tem um problema de desempenho. Use a solução Min / Max.
Sadegh PM 02/09/19 às
25

A resposta do @Matt Dodges me colocou no caminho certo. Mais uma vez obrigado por todas as respostas, o que ajudou muitos caras nesse meio tempo. Funcionou assim:

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
fonte
Esta é a resposta que funciona apenas para a primeira linha. Quebra quando não há f.idcondição.
Tajni
2

Eu usei outra coisa (acho melhor ...) e quero compartilhá-la:

Eu criei uma VIEW que possui uma cláusula "group"

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

Quero dizer ainda que acho que precisamos fazer essa solução, porque fizemos algo errado na análise ... pelo menos no meu caso ... mas às vezes é mais barato fazer isso do que redesenhar tudo ...

Espero que ajude!

Ari Waisberg
fonte
Supondo que haja várias linhas (províncias?) Para cada uma country_code, isso GROUP BYé impróprio. Veja ONLY_FULL_GROUP_BY.
Rick James
Você não precisa criar uma visualização para ser usada exclusivamente para a junção esquerda / interna ?! Pode usar uma subconsulta ou uma WITH x AS (cláusula?
kiradotee 20/01
2

com base em várias respostas aqui, encontrei algo que funcionou para mim e queria generalizar e explicar o que estava acontecendo.

converter:

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

para:

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

a condição que conecta t1 e t2 é movida da ONe para a consulta interna WHERE. o MIN(primary key)ou LIMIT 1garante que apenas 1 linha seja retornada pela consulta interna.

depois de selecionar uma linha específica, precisamos dizer ONqual é a linha. é por isso que ONestá comparando a chave primária da tabela juntada.

você pode jogar com a consulta interna (ou seja, ordem + limite), mas ela deve retornar uma chave primária da linha desejada que informará a ONlinha exata para ingressar.

oriadam
fonte
notas: também funcionará com apenas LIMITou apenas MIN. a ONcondição deve estar na chave primária da tabela unida para evitar impacto no desempenho.
oriadam
1

Eu quero dar uma resposta mais generalizada . Um que lida com qualquer caso quando você deseja selecionar apenas o primeiro item em um LEFT JOIN .

Você pode usar uma subconsulta que GROUP_CONCATS o que você deseja (classificada também!), Depois dividir o resultado do GROUP_CONCAT e pegar apenas o primeiro item, assim ...

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

Como temos DESC como nossa opção ORDER BY , isso retornará um ID de pessoa para alguém como "Zack". Se quiséssemos alguém com o nome "Andy", mudaríamos ORDER BY FirstName DESC para ORDER BY FirstName ASC .

Isso é ágil, pois coloca o poder de encomendar totalmente em suas mãos. Mas, após muitos testes, ele não será dimensionado bem em uma situação com muitos usuários e muitos dados.

No entanto, é útil na execução de relatórios com muitos dados para o administrador.

HoldOffHunger
fonte
O GROUP_CONCATtruque é bom, desde que o número de valores seja limitado. O limite padrão é 1024 caracteres.
Rick James