Pergunta

este é o meu problema sql - há 3 tabelas:

Names         Lists                ListHasNames
Id Name       Id Desc              ListsId  NamesId
=--------     ------------         ----------------
1  Paul       1  Football          1        1
2  Joe        2  Basketball        1        2
3  Jenny      3  Ping Pong         2        1
4  Tina       4  Breakfast Club    2        3
              5  Midnight Club     3        2
                                   3        3
                                   4        1
                                   4        2
                                   4        3
                                   5        1
                                   5        2
                                   5        3
                                   5        4

O que significa que Paul (Id = 1) e Joe (Id = 2) estão na equipa de futebol (Lists.Id = 1), Paul e Jenny na equipe de basquete, etc ...

Agora eu preciso de uma instrução SQL que retorna o Lists.Id de uma combinação específica Nome: Em que as listas são Paul, Joe e Jenny os únicos membros dessa lista? Resposta única Lists.Id = 4 (Breakfast Club) - mas não 5 (Midnight Club) porque Tina está nessa lista, também.

Eu tentei com associações internas e consultas sub:

SELECT Q1.Lists_id FROM

(
SELECT Lists_Id FROM
  names as T1,
  listhasnames as T2
WHERE
  (T1.Name='Paul') and
  (T1.Id=T2.Names_ID) and
   ( (
     SELECT count(*) FROM
      listhasnames as Z1
     where (Z1.lists_id = T2.lists_Id)
    ) = 3)

) AS Q1

INNER JOIN (


SELECT Lists_Id FROM
  names as T1,
  listhasnames as T2
WHERE
  (T1.Name='Joe') and
  (T1.Id=T2.Names_ID) and
  (
    (SELECT count(*) FROM
      listhasnames as Z1
     WHERE (Z1.Lists_id = T2.lists_id)
    ) = 3)

) AS Q2

ON (Q1.Lists_id=Q2.Lists_id)



INNER JOIN (


SELECT Lists_Id FROM
  names as T1,
  listhasnames as T2
WHERE
  (T1.Name='Jenny') and
  (T1.Id=T2.Names_ID) and
  (
    (SELECT count(*) FROM
      listhasnames as Z1
     WHERE (Z1.Lists_id = T2.lists_id)
    ) = 3)

) AS Q3

ON (Q1.Lists_id=Q3.Lists_id)

Parece um pouco complicado, uh? Como otimizar isso? Eu preciso só isso Lists.Id em que nomes específicos estão em (e somente esses nomes e ninguém mais). Talvez com SELECT IN?

Saudações, Dennis

Foi útil?

Solução

SELECT ListsId
FROM ListHasNames a
WHERE NamesId in (1, 2, 3)
AND NOT EXISTS
(SELECT * from ListHasNames b 
WHERE b.ListsId = a.ListsId 
AND b.NamesId not in (1, 2, 3))
GROUP BY ListsId
HAVING COUNT(*) = 3;

Editar : corrigidos graças ao comentário de Chris Gow; o subselect é necessário excluir listas que têm outras pessoas sobre eles. Editar 2 Corrigido o nome da tabela graças ao comentário Dennis'

Outras dicas

Usando a solução da Carl Manaster como um ponto de partida que eu vim com:

SELECT listsid 
FROM listhasnames 
GROUP BY listsid HAVING COUNT(*) = 3
INTERSECT
SELECT x.listsid 
FROM listhasnames x, names n 
WHERE n.name IN('Paul', 'Joe', 'Jenny') 
AND n.id = x.namesid

Atualizado:

select a.ListsId from
(
    --lists with three names only
    select lhn.ListsId, count(*) as count
    from ListHasNames  lhn
    inner join Names n on lhn.NamesId = n.Id 
    group by lhn.ListsId
    having count(*) = 3
) a
where a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Paul'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Joe'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Jenny'))

Eu estava resolvendo um problema recentemente que pode funcionar bem para o seu caso também. Ele pode ser um exagero.

Eu levei a abordagem de criar uma lista de associações candidatos que podem ser a solução correta, e em seguida, usando um cursor ou fila de tabela para percorrer as soluções corretas que possam fazer a validação completa.

No meu caso esta foi implementada fazendo como

select
ParentId
count(*) as ChildCount
checksum_agg(checksum(child.*) as ChildAggCrc
from parent join child on parent.parentId = child.parentId

Em seguida, você pode comparar a contagem e soma de verificação agregada contra os seus dados de pesquisa (isto é, os seus 3 nomes para verificar). Se nenhuma linha corresponderem, você está garantido para ter nenhum resultado. Se alguma linha combina com você pode, então, passar e fazer uma junção de que ParentId específica para validar se houver qualquer discrepância entre os conjuntos de linhas.

Claro como lama? :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top