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 / 344457
Accepted
sterroso
sterroso
Asked: 2024-12-31 03:29:27 +0800 CST2024-12-31 03:29:27 +0800 CST 2024-12-31 03:29:27 +0800 CST

PostgreSQL relacionamento um para muitos transpondo segunda tabela

  • 772

Tenho duas tabelas, para simplificar vamos chamá-las employeesde e employee_comms:

employee:

eu ia nome departamento
1 Jane Manutenção
2 João Projeto

employee_comms:

eu ia ID do contato tipo_de_comunicação valor_de_comunicação
1 1 e-mail [email protegido]
2 1 telefone 555 324 6573
3 2 e-mail [email protegido]
4 2 telefone 555 201 5843

Atualmente, comms_typesó pode ser phoneou email.

Escrevi uma consulta para filtrar funcionários por departamento e mostrar e-mail e telefone de cada um:

SELECT
  d.id,
  d.name,
  d.dept,
  p.comms_value as phone,
  e.comms_value as email
FROM
  (
    SELECT *
    FROM employee
    WHERE dept = 'Design'
  ) AS d
  LEFT JOIN (
    SELECT contact_id, comms_value
    FROM employee_comms
    WHERE comms_type = 'phone'
  ) AS p ON (d.id = p.contact_id)
  LEFT JOIN (
    SELECT contact_id, comms_value
    FROM employee_comms
    WHERE comms_type = 'email'
  ) AS e ON (d.id = e.contact_id);

O resultado é uma tabela onde a segunda tabela é transposta com linhas únicas e exclusivas onde nenhum employeevalor é duplicado ( id, name):

eu ia nome departamento telefone e-mail
2 João Projeto [email protegido] 555 201 5843

Atualmente, ele resolve minhas necessidades, mas continuo pensando que deveria haver uma solução mais eficiente, já que estou consultando a employee_commstabela duas vezes e isso pode se tornar um problema de desempenho quando a employee_commstabela ficar grande (e isso vai acontecer).

P: Existe uma abordagem mais eficiente?

postgresql
  • 1 1 respostas
  • 33 Views

1 respostas

  • Voted
  1. Best Answer
    Vérace
    2025-01-02T13:02:19+08:002025-01-02T13:02:19+08:00

    Você poderia fazer algo assim (todo o código abaixo está disponível no fiddle aqui ):

    SELECT
      e.id, e.name, e.dept,
      STRING_AGG(ec.comm_type, ', '  ORDER BY ec.e_id, ec.comm_type) AS ,
      STRING_AGG(ec.comm_value, ', ' ORDER BY ec.e_id, ec.comm_type)
    FROM
      employee_comm ec
    JOIN employee e ON ec.e_id = e.id
    WHERE ec.comm_type IN ('phone', 'email') AND e.dept = 'Design'
    GROUP BY e.id, e.name, e.dept
    ORDER BY e.id;
    

    Resultado (adicionei alguns outros dados aos seus):

    id name  dept        Ct Types       Ct values
     2  Joe   Design      email, phone   [email protected], 555 201 5843
    

    Confira os ANALYZE (EXPLAIN...trechos que incluí nos meus violinos.

    Há uma série de problemas com seu esquema.

    Você está usando o ( temido ) modelo EAV (Entity Attribute Value) - isso é (geralmente - > 99,99...%) um bad idea™! Veja aqui o que tem a dizer sobre isso - também aqui (e links dentro). Você está misturando números de telefone e e-mails no mesmo campo - em vez de misturá-los na mesma tabela - o que é bom.

    No entanto, você tem mais de um tipo de comunicação por pessoa - então o que você precisa é de uma associative entity(também conhecida como tabela de junção (há > 15 outros nomes na página Wiki).

    OU você pode ter uma tabela separada para cada tipo de comunicação - ou seja, para telefone(s), e-mail(s)... essa também é uma abordagem válida - se não houver centenas desses tipos.

    Então, pegando a segunda ideia primeiro, criei uma emailtabela (e uma phonetabela - não mostrada, veja o violino aqui ) como segue:

    CREATE TABLE email
    (
      
      e_id      SMALLINT NOT NULL,
      e_value   TEXT     NOT NULL,
    
      CONSTRAINT email_pk
        PRIMARY KEY (e_id, e_value),  -- nobody can have the same email twice
      
      CONSTRAINT email_val_ck 
        CHECK (e_value ~ '^[a-zA-Z0-9]{1,8}@[a-zA-Z0-9-]{2,8}\.[a-zA-Z0-9]{3}') -- simple check
      
    );
    

    Agora, observe que eu posso implementar CHECK CONSTRAINTs - estes são um good thing™! Seu banco de dados é seu último bastião de defesa contra dados ruins, então usar uma regex para verificar novamente se você não tem números de telefone ou e-mails inválidos deve (pelo menos ajudar a) manter seus dados válidos!

    Então, você terá consultas como esta:

    SELECT 
      e.id,
      e.name, e.dept, 
      COALESCE(em.e_value, 'NO EMAIL') AS email, 
      COALESCE(ph.p_value, 'NO PHONE') AS phone
    FROM
      employee e
    LEFT JOIN email em ON e.id = em.e_id
    LEFT JOIN phone ph ON e.id = ph.e_id
    ORDER BY e.name, ph.p_value;
    

    Resultado:

    id name  dept         email             phone
    1  Jane  Maintenance  [email protected]  555 324 6578
    2  Joe   Design       [email protected]  555 201 5843
    3  No E  No Email     NO EMAIL          000 000 0000
    4  No P  No phone     [email protected]   NO PHONE
    

    Você pode adicionar mais tabelas conforme aqui - adicionei um prioritycampo se você quiser usar a 1ª prioridade do usuário como método de contato preferencial.

    Você tem então:

    SELECT
      ec.e_id AS "Emp ID",
      STRING_AGG(cm_name,  ', ' ORDER BY ec.e_id, ec.m_priority) AS "Order",
      STRING_AGG(ec.m_val, ', ' ORDER BY ec.e_id, ec.m_priority) AS "Comm method",
      e.e_comment AS "Comment"
    FROM 
      emp_comm ec
    JOIN comm_method cm ON ec.m_id = cm.id
    JOIN employee e     ON ec.e_id = e.id
    WHERE cm.cm_name IN ('Email', 'Phone')  -- ET not here because he has neither E nor P!
    GROUP BY ec.e_id, e.e_comment  
    ORDER BY ec.e_id;
    

    Resultado:

    Emp ID Order         Comm method                    Comment
    1      Email, Phone  [email protected], 555 324 6573 Email and Phone
    2      Phone, Email  555 201 5843, [email protected] Email and Phone
    3      Phone         000 000 0000                   Phone, no Email
    4      Email         [email protected]                Email, no Phone
    

    Basicamente, você pode obter muito mais informações muito mais rápida e facilmente ao não usar o paradigma EAV ( antipadrão ) e seus stakeholders/clientes também ficarão mais felizes! Um EAV tem complexidade ON^2 (ou ON!) - conforme você adiciona mais registros às suas tabelas EAV, suas consultas rapidamente se tornarão monstros incontroláveis ​​- enquanto que se você distinguir claramente suas entidades, sua vida e suas consultas serão muito mais fáceis!

    • 1

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

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