Liste des e-mails des utilisateurs regardant une catégorie spécifique

J’ai une requête Data Explorer qui exécute ceci :

SELECT * FROM category_users WHERE category_id = '10'

Cela me donne un résultat qui ressemble à ceci :

Comment puis-je également afficher l’adresse e-mail des utilisateurs dans cette sortie ?

(Pour éviter les discussions sur la confidentialité, je précise ce qui suit : nous utilisons un Discourse privé pour les membres payants, qui ont individuellement donné leur consentement à l’utilisation de leurs informations personnelles dans le but de leur fournir des services. Nous disposons de systèmes qui ne sont pas interopérables de manière automatisée et nous utilisons les adresses e-mail pour relier manuellement les utilisateurs entre deux systèmes différents.)

Vous devez joindre la table user_emails en fonction de l’identifiant user_id provenant de la table category_users. Essayez quelque chose comme ceci :

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

Merci, Simon, et désolé d’avoir mis autant de temps à te répondre !

J’ai justement essayé ta requête et elle fait exactement ce que je voulais ! :folded_hands:

Existe-t-il un moyen d’obtenir les mêmes données à l’échelle du site, et pas seulement pour une catégorie spécifique ?

Je pose la question car nous prévoyons de restructurer notre forum en utilisant des catégories plus granulaires, ce qui rend mon projet de créer une requête Data Explorer distincte pour chaque catégorie moins viable.

J’ai trouvé comment demander plusieurs catégories en utilisant quelque chose comme ceci :

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

Mais je devrai me souvenir de mettre à jour la requête après avoir modifié les catégories, ce que j’ai toutes les chances d’oublier :smiley:

Vous pouvez simplement supprimer le filtre category_id = <number> en entier, de sorte que la requête ressemblerait à ceci :

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

L’Explorateur de données affichera les noms de catégories pour vous, mais ils n’apparaîtront pas lors de l’exportation des résultats. Si cela pose problème, vous pouvez ajouter explicitement le nom de la catégorie en tant que colonne, comme ceci :

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

Merci pour votre question @simonk !

Je ne comprends pas pourquoi vous avez utilisé WHERE ue.primary = true au lieu de AND ue.primary = true. La requête nécessite-t-elle toujours un WHERE ?

Pas exactement. Ce serait peut-être plus clair si nous reformattions légèrement la requête de @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)

Les conditions category_id et ue.primary font toutes deux partie de la clause WHERE, reliées par AND. Si vous supprimez l’une des conditions, vous supprimez le AND, mais vous conservez la clause WHERE.

La plupart des requêtes SQL simples suivent cette forme :

SELECT <choses_désirées>
FROM <tables>
WHERE <conditions_de_filtrage>

Vous pouvez omettre la clause WHERE entièrement, mais dans ce cas, vous obtiendrez toutes les lignes des tables que vous avez spécifiées.

Voici votre requête originale (reformatée) :

SELECT *
FROM category_users
WHERE category_id = '10'
  • SELECT *” signifie que vous souhaitez que la requête renvoie toutes les colonnes de toutes les tables impliquées.

  • FROM category_users” indique la table que vous souhaitez interroger. La table category_users contient des lignes ressemblant à ceci :

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

    category_id et user_id sont appelés clés étrangères car elles pointent vers une ligne dans une autre table (dans ce cas, les tables categories et users). Ainsi, les 3 lignes ci-dessus signifient que l’utilisateur avec l’ID 1 surveille les catégories 1 et 3, et que l’utilisateur avec l’ID 2 surveille la catégorie 1. Le notification_level indique s’ils sont en mode Surveillance, Surveillance du premier message ou Suivi.

  • WHERE category_id = '10'” signifie que vous n’êtes intéressé que par les lignes où la valeur dans la colonne category_id est 10. Sans cette ligne, vous obtiendriez toutes les lignes de la table category_users.

@simon vous a fourni une nouvelle version qui ajoute l’adresse e-mail de l’utilisateur :

Cette requête a apporté quelques modifications par rapport à votre version originale, pour deux raisons : les adresses e-mail sont stockées dans une table différente (la table user_emails), et les utilisateurs peuvent avoir plusieurs adresses e-mail.

  • Dans la clause SELECT :

    • cu.*” signifie « toutes les colonnes de la table cu »
    • ue.email” signifie « la colonne email de la table ue »
  • Dans la clause FROM :

    • La table category_users possède maintenant un alias, “cu”, ce qui évite de taper à nouveau si vous devez y faire référence plusieurs fois.

    • Nous avons JOINé la table user_emails et lui avons donné l’alias ue.

      La table user_emails contient des lignes comme celle-ci :

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

      Cela signifie que l’utilisateur avec l’ID 1 a 2 adresses e-mail : alex@example.com (l’adresse principale) et alex@other.example.com (une adresse secondaire). L’utilisateur avec l’ID 2 n’a qu’une seule adresse.

      Lorsque vous JOINez 2 tables en SQL, vous devez généralement indiquer à la base de données quelle est la condition de jointure. Si vous ne le faites pas, la base de données ne sait pas quelles valeurs dans chacune des tables doivent correspondre, et vous vous retrouverez avec toutes les combinaisons possibles de lignes des 2 tables. Si vous écriviez cette requête :

      SELECT *
      FROM category_users
      JOIN user_emails
      

      …avec les données d’exemple ci-dessus, vous obtiendriez 9 lignes : vous obtiendriez la première ligne de category_users trois fois, une fois avec chacune des lignes de user_emails, puis de même pour la deuxième ligne de category_users trois fois, et enfin la troisième ligne de category_users trois fois.

      La condition de jointure indique généralement à la base de données quelle colonne dans les 2 tables représente la même valeur. Dans ce cas, les colonnes category_users.user_id et user_emails.user_id représentent toutes deux la même valeur. En écrivant ON ue.user_id = cu.user_id après JOIN user_emails ue, nous indiquons à la base de données d’apparier les lignes de user_emails avec les lignes appropriées de category_users.

    • Même avec la condition de jointure, nous obtiendrons toujours 4 lignes pour l’utilisateur avec l’ID 1, car il surveille 2 catégories et possède 2 adresses e-mail : nous obtiendrons une ligne pour chaque combinaison. @simon a donc ajouté une condition supplémentaire à la clause WHERE afin que la requête ne renvoie que les lignes avec l’adresse e-mail principale de l’utilisateur. Cette condition s’ajoute en plus de la condition déjà existante (restriction de l’ID de catégorie) : pour être renvoyées, les lignes doivent avoir category_id = '10' ET ue.primary = true.

Ensuite, comme vous ne souhaitiez pas limiter votre recherche à une seule catégorie, vous avez simplement dû supprimer le filtre category_id. Vous ne voulez pas supprimer toute la clause WHERE, car vous souhaitez toujours ne renvoyer que les adresses e-mail principales. En d’autres termes, votre condition de filtrage est passée de :

category_id = '10' AND ue.primary = true

à

ue.primary = true

Ouf ! J’espère que tout cela est clair :nerd_face:

Merci pour ce post incroyablement détaillé, @simonk ! Je dois admettre que le SQL reste un mystère complet pour moi, et ton explication m’a vraiment aidé à commencer à le comprendre. Je te suis vraiment reconnaissant d’avoir pris le temps de m’aider ! :folded_hands: