正在关注特定类别的用户电子邮件列表

我有一条 Data Explorer 查询语句,内容如下:

SELECT * FROM category_users WHERE category_id = '10'

该查询返回的结果如下所示:

如何在输出结果中同时显示用户的电子邮件地址?

(为避免引发隐私讨论,特此说明:我们使用的是面向付费会员的私有 Discourse 实例,这些会员已单独同意我们使用其个人信息以提供相关服务。由于我们使用的系统之间无法自动互通,因此我们通过电子邮件手动将两个不同系统中的用户进行关联。)

您需要根据 category_users 表中的 user_id 连接 user_emails 表。请尝试以下方法:

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

谢谢,Simon,很抱歉回复晚了!

我刚刚尝试了你的查询,它完全符合我的预期!:folded_hands:

有没有办法获取全站相同的数据,而不仅仅是特定类别的?

我之所以这样问,是因为我们计划重组论坛,使用更细粒度的类别,这使得我为每个类别单独创建数据探索查询的计划变得不太可行。

我已经知道如何通过类似下面的方式请求多个类别:

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

但我需要在更改类别后记得更新查询,而我很容易忘记这样做 :smiley:

您可以直接移除 category_id = <number> 过滤条件,这样查询语句将类似于:

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

数据探索器会为您显示分类名称,但在导出结果时这些名称不会显示。如果这对您来说是个问题,您可以显式地将分类名称作为一列添加,类似于这样:

SELECT
    c.name,
    cu.*,
    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

感谢你的提问 @simonk

我不明白你为什么使用 WHERE ue.primary = true 而不是 AND ue.primary = true。查询是否总是需要 WHERE 子句?

不完全是。如果我们稍微重新格式化 @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)

category_idue.primary 条件都是 WHERE 子句的一部分,通过 AND 连接。如果你移除其中一个条件,你需要移除 AND,但保留 WHERE 子句。

大多数简单的 SQL 查询遵循以下形式:

SELECT <你想要的东西>
FROM <表>
WHERE <过滤条件>

你可以完全省略 WHERE 子句,但那样你会从指定的表中获取每一行

这是你原始的查询(重新格式化后):

SELECT *
FROM category_users
WHERE category_id = '10'
  • SELECT *” 意味着你希望查询返回所有涉及表中的每一列。

  • FROM category_users” 表示你想要查询的表。category_users 表包含类似这样的行:

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

    category_iduser_id 被称为外键,因为它们指向另一张表中的行(在本例中是 categoriesusers 表)。因此,上面的 3 行表示 id 为 1 的用户正在关注类别 13,而 id 为 2 的用户正在关注类别 1notification_level 表示他们是处于关注关注首帖还是追踪状态。

  • WHERE category_id = '10'” 意味着你只对 category_id 列值为 10 的行感兴趣。如果没有这一行,你会从 category_users 表中获取每一行

@simon 提供了一个新版本,添加了用户的电子邮件地址:

这个查询对你的原始查询做了一些修改,原因有两点:电子邮件地址存储在不同的表(user_emails 表)中,并且用户可以拥有多个电子邮件地址。

  • SELECT 子句中:

    • cu.*” 表示"cu 表的所有列”
    • ue.email” 表示"ue 表的 email 列”
  • FROM 子句中:

    • category_users 表现在有一个别名"cu",如果你需要多次引用它,可以节省一些输入。

    • 我们已JOINuser_emails 表,并给它起了别名 ue

      user_emails 表包含类似这样的行:

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

      这意味着 id 为 1 的用户有两个电子邮件地址:alex@example.com(主地址)和 alex@other.example.com(次要地址)。id 为 2 的用户只有一个地址。

      当你在 SQL 中 JOIN 两个表时,通常需要告诉数据库连接条件是什么。如果你不这样做,数据库就不知道每个表中的哪些值应该匹配,最终你会得到两个表中所有可能的行组合。如果你写了这个查询:

      SELECT *
      FROM category_users
      JOIN user_emails
      

      …使用上面的示例数据,你会得到 9 行:你会得到 category_users 的第一行三次,每次对应一个 user_emails 行;同样,你会得到 category_users第二行三次,最后得到 category_users第三行三次。

      连接条件通常告诉数据库两个表中的哪一列代表相同的值。在本例中,category_users.user_id 列和 user_emails.user_id 列都代表相同的值。通过在 JOIN user_emails ue 之后编写 ON ue.user_id = cu.user_id,我们告诉数据库将 user_emails 行与相应的 category_users 行匹配。

    • 即使有了 JOIN 条件,我们仍然会为 id 为 1 的用户返回 4 行,因为他们正在关注 2 个类别并且有 2 个电子邮件地址——我们会为每种组合返回一行。因此,@simonWHERE 子句中添加了额外的条件,以便查询只返回用户主电子邮件地址的行。此条件是已有条件(限制类别 ID)之外的——为了被返回,行必须满足 category_id = '10' 并且 ue.primary = true

然后,因为你不想将搜索限制为单个类别,你只需要移除 category_id 过滤器。你不想移除整个 WHERE 子句,因为你仍然只想返回主电子邮件地址。换句话说,你的过滤条件从:

category_id = '10' AND ue.primary = true

变为

ue.primary = true

呼!希望这一切都讲得通 :nerd_face:

感谢您如此详尽的帖子,@simonk!我必须承认,SQL 对我来说完全是一个谜,而您的解释对我开始理解它非常有帮助。非常感谢您抽出时间帮助我!:folded_hands: