AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 133271
Accepted
user3202098
user3202098
Asked: 2016-03-25 03:59:49 +0800 CST2016-03-25 03:59:49 +0800 CST 2016-03-25 03:59:49 +0800 CST

SQL obtendo IDs de uma tabela com várias entradas em outra tabela

  • 772

Em meu banco de dados tenho duas tabelas: Persons e PersonAttributes.
Uma Pessoa pode ter vários Atributos associados a ela.

O que eu gostaria de fazer é obter uma lista de todas as Persons(/IDs) que possuem pelo menos os atributos especificados associados a elas na tabela PersonAttributes.

Para um atributo, isso pode ser feito facilmente com apenas uma instrução WHERE. Meu problema é que quero fazer isso para vários atributos.

A única solução que encontrei é fazer um SELECT para cada atributo e, em seguida, juntá-los. Embora eu possa criar essa consulta programaticamente, ela parece bastante complexa e esperava que houvesse uma solução mais fácil.

Para melhor entendimento aqui está um SQLFiddle incluindo minha solução para 3 atributos.

EDIT: Alterado o link SQLFiddle. A consulta agora se parece com isso:

SELECT Persons.ID 
FROM Persons
  JOIN (SELECT * FROM PersonAttributes WHERE PersonAttributes.Attr = 'b') t1
    ON Persons.ID = t1.ID
  JOIN (SELECT * FROM PersonAttributes WHERE PersonAttributes.Attr = 'c') t2
    ON t1.ID = t2.ID
  JOIN (SELECT * FROM PersonAttributes WHERE PersonAttributes.Attr = 'd') t3
    ON t2.ID = t3.ID;
performance query-performance
  • 1 1 respostas
  • 6179 Views

1 respostas

  • Voted
  1. Best Answer
    Andriy M
    2016-03-26T05:24:06+08:002016-03-26T05:24:06+08:00

    Aparentemente, sua PersonAttributesmesa foi projetada usando o modelo EAV . Esse modelo tem a vantagem de ser facilmente extensível: os atributos são armazenados como linhas e é fácil adicionar novas linhas. Entretanto, a consulta a esse tipo de tabela é mais difícil do que aquelas elaboradas de forma tradicional (atributos armazenados em colunas).

    Sua solução é bastante ilustrativa de quanto mais problemas pode ser realizar uma tarefa bastante simples com uma tabela modelada por EAV. Na verdade, é uma das maneiras comuns de resolver um problema como o seu, embora eu sugira que você tente reescrevê-lo sem usar tabelas derivadas – assim:

    SELECT p.ID 
    FROM Persons AS p
      JOIN PersonAttributes AS paB ON p.ID = paB.ID
      JOIN PersonAttributes AS paC ON p.ID = paC.ID
      JOIN PersonAttributes AS paD ON p.ID = paD.ID
    WHERE paB.Attr = 'b'
      AND paC.Attr = 'c'
      AND paD.Attr = 'd';
    

    O desempenho provavelmente permanecerá o mesmo da sua sintaxe, mas sem tornar a consulta mais rápida, essa reescrita pelo menos a tornará mais concisa e sem dúvida mais legível.

    Dito isto, existe outro método, bastante comum também, que você pode empregar, que pode oferecer melhor desempenho à medida que o número de atributos aumenta. Ele usa agrupamento e agregação:

    SELECT
      ID
    FROM
      PersonAttributes
    WHERE
      Attr IN ('b', 'c', 'd')
    GROUP BY
      ID
    HAVING
      COUNT(*) = 3
    ;
    

    Por esse método, todas as linhas que possuem qualquer um dos atributos especificados são recuperadas e agrupadas por ID. Para determinar os grupos (pessoas) com todos os três atributos, um filtro HAVING é introduzido para comparar o número de linhas * em cada grupo com o número total de atributos na INlista.

    O método pode ser ligeiramente generalizado se você puder armazenar os atributos a serem pesquisados ​​em uma tabela (temporária). Veja como ficaria nesse caso:

    SELECT
      pa.ID
    FROM
      PersonAttributes AS pa
      INNER JOIN QueriedAttributes AS qa ON pa.Attr = qa.Attr
    GROUP BY
      pa.ID
    HAVING
      COUNT(*) = (SELECT COUNT(*) FROM QueriedAttributes)
    ;
    

    Nenhuma cláusula WHERE aqui – ela é substituída pela junção à tabela de atributos consultados, e o número total de atributos necessários para correspondência é derivado da mesma tabela em vez de ser codificado.

    Esse tipo de problema é comumente conhecido como divisão relacional . É discutido em detalhes neste artigo de Joe Celko:

    • Divididos nós permanecemos: o SQL da divisão relacional

    * Esta implementação particular do método de agrupamento assume que sempre há uma linha por atributo por pessoa, então COUNT(*)funciona corretamente. Se atributos do mesmo tipo puderem ser repetidos por pessoa, ou posteriormente serão permitidos, use COUNT(DISTINCT Attr)em vez disso.

    • 3

relate perguntas

  • Existe um ganho de desempenho ao manipular dados com procedimentos armazenados em vez de alimentá-los em funções após a recuperação?

  • Como você ajusta o MySQL para uma carga de trabalho pesada do InnoDB?

  • Como determinar se um Índice é necessário ou necessário

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve