Lista de e-mails de usuários que assistem a uma categoria específica

Tenho uma consulta no Data Explorer que executa o seguinte:

SELECT * FROM category_users WHERE category_id = '10'

Isso me retorna um resultado parecido com este:

Como posso também exibir o e-mail dos usuários nessa saída?

(Para evitar discussões sobre privacidade, deixe-me observar o seguinte: utilizamos um Discourse privado para membros pagantes, que individualmente consentiram com o uso de suas informações pessoais para fins de entrega de serviços a eles. Temos sistemas que não interoperam de forma automatizada e usamos e-mails para conectar manualmente os usuários em dois sistemas diferentes.)

Você precisa fazer um JOIN na tabela user_emails com base no user_id da tabela category_users. Tente algo assim:

SELECT
cu.*,
ue.email
FROM category_users cu
JOIN user_emails ue
ON ue.user_id = cu.user_id
WHERE category_id = '10'
AND ue.primary = true

Obrigado, Simon, e peço desculpas por ter demorado tanto para responder!

Acabei de testar sua consulta e ela faz exatamente o que eu queria! :folded_hands:

Existe uma maneira de obter os mesmos dados em todo o site, e não apenas para uma categoria específica?

Faço essa pergunta porque estamos planejando reestruturar nosso fórum para usar categorias mais granulares, o que torna meu plano de criar uma consulta separada do Data Explorer para cada categoria menos viável.

Descobri como solicitar múltiplas categorias usando algo como:

WHERE (category_id = '48') OR (category_id = '66') OR (category_id = '57')

Mas terei que me lembrar de atualizar a consulta após alterar as categorias, e é muito provável que eu esqueça de fazer isso :smiley:

Você pode simplesmente remover o filtro category_id = <number> por completo, de modo que a consulta ficaria mais ou menos assim:

SELECT
    cu.*,\n    ue.email
FROM category_users cu
JOIN user_emails ue
  ON ue.user_id = cu.user_id
WHERE ue.primary = true

O Data Explorer exibirá os nomes das categorias para você, mas eles não aparecerão ao exportar os resultados. Se isso for um problema para você, pode adicionar explicitamente o nome da categoria como uma coluna, algo assim:

SELECT
    c.name,\n    cu.*,\n    ue.email
FROM category_users cu
JOIN user_emails ue
  ON ue.user_id = cu.user_id
JOIN categories c
  ON cu.category_id = c.id
WHERE ue.primary = true
ORDER BY c.name

Obrigado pela pergunta, @simonk!

Não entendi por que você usou WHERE ue.primary = true em vez de AND ue.primary = true. A consulta sempre exige um WHERE?

Não exatamente. Talvez fique mais claro se reformatarmos um pouco a consulta do @simon:

SELECT
    cu.*,
    ue.email
FROM category_users cu
JOIN user_emails ue ON ue.user_id = cu.user_id
WHERE (category_id = '10' AND ue.primary = true)

As condições category_id e ue.primary fazem parte da cláusula WHERE, unidas por AND. Se você remover uma das condições, remove o AND, mas mantém a cláusula WHERE.

A maioria das consultas SQL simples segue este formato:

SELECT <coisas_que_você_quer>
FROM <tabelas>
WHERE <condições_de_filtro>

Você pode omitir a cláusula WHERE por completo, mas aí receberá todas as linhas das tabelas que especificou.

Aqui está sua consulta original (reformatada):

SELECT *
FROM category_users
WHERE category_id = '10'
  • SELECT *” significa que você quer que a consulta retorne todas as colunas de todas as tabelas envolvidas.

  • FROM category_users” indica a tabela que você deseja consultar. A tabela category_users contém linhas mais ou menos assim:

    id category_id user_id notification_level
    1 1 1 3
    2 1 2 3
    3 3 1 3

    category_id e user_id são chamados de chaves estrangeiras porque apontam para uma linha em outra tabela (neste caso, as tabelas categories e users). Portanto, as 3 linhas acima significam que o usuário com id 1 está assistindo às categorias 1 e 3, e o usuário com id 2 está assistindo à categoria 1. O notification_level indica se eles estão Assistindo, Assistindo à Primeira Postagem ou Acompanhando.

  • WHERE category_id = '10'” significa que você só está interessado nas linhas onde o valor na coluna category_id é 10. Sem essa linha, você receberia todas as linhas da tabela category_users.

@simon forneceu uma nova versão que adicionou o endereço de e-mail do usuário:

Essa consulta fez algumas alterações em relação à sua original, por 2 motivos: os endereços de e-mail são armazenados em uma tabela diferente (a tabela user_emails), e os usuários podem ter mais de um endereço de e-mail.

  • Na cláusula SELECT:

    • cu.*” significa “todas as colunas da tabela cu
    • ue.email” significa “a coluna email da tabela ue
  • Na cláusula FROM:

    • A tabela category_users agora tem um alias, “cu”, o que economiza digitação se precisar referenciá-la mais de uma vez.

    • Fizemos um JOIN na tabela user_emails e demos a ela o alias ue.

      A tabela user_emails contém linhas assim:

      id user_id email primary
      1 1 alex@example.com true
      2 1 alex@other.example.com false
      3 2 simon@example.com true

      O que significa que o usuário com id 1 tem 2 endereços de e-mail: alex@example.com (o endereço principal) e alex@other.example.com (um endereço secundário). O usuário com id 2 tem apenas um endereço.

      Quando você faz um JOIN de 2 tabelas em SQL, normalmente precisa dizer ao banco de dados qual é a condição de junção. Se não fizer isso, o banco de dados não saberá quais valores em cada tabela devem ser combinados, e você acabará com todas as combinações possíveis de linhas nas 2 tabelas. Se você escrevesse esta consulta:

      SELECT *
      FROM category_users
      JOIN user_emails
      

      …com os dados de exemplo acima, receberia 9 linhas: receberia a primeira linha de category_users três vezes, uma vez com cada linha de user_emails, depois, da mesma forma, receberia a segunda linha de category_users três vezes e, finalmente, receberia a terceira linha de category_users três vezes.

      A condição de junção normalmente diz ao banco de dados qual coluna nas 2 tabelas representa o mesmo valor. Neste caso, a coluna category_users.user_id e a coluna user_emails.user_id representam o mesmo valor. Ao escrever ON ue.user_id = cu.user_id após JOIN user_emails ue, dizemos ao banco de dados para combinar as linhas de user_emails com as linhas apropriadas de category_users.

    • Mesmo com a condição de JOIN, ainda receberemos 4 linhas para o usuário com ID 1, porque ele está assistindo a 2 categorias e tem 2 endereços de e-mail — receberemos uma linha para cada combinação. Então, @simon adicionou uma condição extra à cláusula WHERE para que a consulta retornasse apenas linhas com o endereço de e-mail principal do usuário. Essa condição é adicional à condição que já existia (restringindo o ID da categoria) — para que as linhas sejam retornadas, elas devem ter category_id = '10' E ue.primary = true.

Então, como você não queria restringir sua pesquisa a uma única categoria, precisava apenas remover o filtro category_id. Você não quer remover toda a cláusula WHERE, porque ainda deseja retornar apenas endereços de e-mail principais. Em outras palavras, sua condição de filtro mudou de:

category_id = '10' AND ue.primary = true

para

ue.primary = true

Ufa! Espero que tudo isso faça sentido :nerd_face:

Obrigado pelo post incrivelmente detalhado, @simonk! Preciso admitir que SQL é um mistério completo para mim, e sua explicação foi realmente útil para começar a entendê-lo. Agradeço muito por ter tirado um tempo para me ajudar! :folded_hands: