本指南是 数据探索教程 - 第 1 部分 - 编写您的第一个查询 的延续。
现在您已经了解了数据探索的实际运作方式,让我们来谈谈一些 SQL 基础知识,这些知识将有助于您在数据探索之旅中更好地使用它。
SQL 语句
您在 Discourse 数据库中需要执行的所有操作都是通过 SQL 语句完成的。
SQL 关键字不区分大小写:
select与SELECT相同,但在本教程中,为了最佳实践,我们将所有 SQL 关键字都写成大写形式。
以下是一些您可能使用的常见 SQL 语句。请注意,我们在 之前的示例查询 中已经使用过其中一些:
- SELECT:用于从数据库中选取数据。返回的数据存储在结果表中,称为结果集。
SELECT column1, column2, ...
FROM table_name;
- WHERE:用于筛选记录。
SELECT column1, column2, ...
FROM table_name
WHERE condition;
- ORDER BY:用于按升序或降序对结果集进行排序。
SELECT column1, column2, ...
FROM table_name
ORDER BY column1, column2, ... ASC|DESC;
- COUNT:返回符合指定条件的行数。
SELECT COUNT(column_name)
FROM table_name
WHERE condition;
- GROUP BY:通常与聚合函数(COUNT、MAX、MIN、SUM、AVG)一起使用,按一个或多个列对结果集进行分组。
SELECT column_name, COUNT(*)
FROM table_name
GROUP BY column_name;
- HAVING:用于筛选 GROUP BY 操作的结果。由于 WHERE 关键字不能与聚合函数(例如:
COUNT()、MAX()、MIN()、SUM()、AVG())一起使用,因此 SQL 中使用了 HAVING 子句。
SELECT column_name, COUNT(*)
FROM table_name
GROUP BY column_name
HAVING COUNT(*) > value;
- IN:用于检查某个值是否存在于一组值中或由子查询返回。它是多个
OR条件的简写形式,使您的 SQL 查询更加简洁且易于阅读。
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);
在数据探索中编写 SQL 查询时,分号不是必需的。
PostgreSQL
Discourse 目前使用 PostgreSQL 13 版本作为其数据库,这是一个开源的关系型数据库管理系统(RDBMS),强调可扩展性和 SQL 合规性。
若要深入了解 PostgreSQL,您可以阅读 PostgreSQL 官方文档。特别是,在编写 SQL 查询时,您可能觉得以下部分特别相关:查询、数据类型 和 函数。
数据库表
Discourse 数据库由表组成。表是关系型数据库管理系统中存储数据的基本方式。
Discourse 中的每个表都有一个名称(例如 posts 或 topics),每个表包含带有数据的记录(行)。表中的列通常被称为字段。
Discourse 数据库包含 240 多个表!
数据探索器在查询编辑 UI 面板中首先列出 9 个最重要的表,您可以点击查看所有表的列结构和类型:
数据库模式
在 PostgreSQL 以及其他关系型数据库中,主键和外键的概念至关重要。它们用于建立表之间的关系,并确保数据的一致性和完整性。
主键
主键是表中的一列或一组列,用于唯一标识该表中的每一行。表中的两行不能具有相同的主键值。主键值不能为 NULL,且必须唯一。主键用于对表中的数据进行索引,从而大大加快数据检索速度。在 Discourse 模式探索器中,表的主键始终列在最前面。
示例:posts 表的主键是 id 字段。
外键
外键是表中的一列或一组列,用于在两个表之间建立链接。它作为表之间的交叉引用,因为它引用了另一个表的主键,从而在它们之间建立链接。包含外键的表称为子表,包含主键的表称为引用表或父表。在 Discourse 模式探索器中,外键由文本 fkey 后跟父表名称表示。
示例:在 posts 表中,user_id 是 users 表的外键。
使用主键和外键
当您在 数据探索器 中编写查询时,可以利用这些主键和外键将表 JOIN 在一起,以获取更复杂的数据。例如,如果您想查找某个用户发布的所有帖子,可以编写如下查询:
SELECT p.id, p.created_at, p.raw
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE u.username = 'username_here'
在此查询中,我们使用 posts 表中的 user_id 外键与 users 表中的 id 主键进行连接。这样我们可以找到某个用户发布的所有帖子。
SQL JOIN
如前所述,JOIN 语句用于基于两个或多个表之间的相关列组合行。
值得注意的是,SQL 中有多种类型的 JOIN 语句,每种都有不同的用途:
-
INNER JOIN:
INNER JOIN关键字选择两个表中具有匹配值的记录。它返回两个表中匹配的行。当您只想返回两个表中都有匹配的记录时,请使用INNER JOIN。当使用JOIN而未指定连接类型(如LEFT、RIGHT或FULL)时,默认情况下为INNER JOIN。 -
LEFT (OUTER) JOIN:
LEFT JOIN关键字返回左表(table1)中的所有记录以及右表(table2)中的匹配记录。如果没有匹配,右侧的结果为 NULL。当您想返回左表中的所有记录以及右表中的匹配记录时,请使用LEFT JOIN。如果没有匹配,右侧的结果为 NULL。 -
RIGHT (OUTER) JOIN:
RIGHT JOIN关键字返回右表(table2)中的所有记录以及左表(table1)中的匹配记录。如果没有匹配,左侧的结果为 NULL。当您想返回右表中的所有记录以及左表中的匹配记录时,请使用RIGHT JOIN。如果没有匹配,左侧的结果为 NULL。 -
FULL (OUTER) JOIN:
FULL JOIN关键字在左表(table1)或右表(table2)中任一表有匹配时返回所有记录。当您想返回任一表中有匹配的所有记录时,请使用FULL JOIN。
您还可以组合多个 JOIN 语句以连接更多表。例如,我们可以将 topic_tags 表与 topics 表连接,然后将 tags 表与 topic_tags 表连接,以获取与主题关联的标签名称。
SELECT
t.id AS topic_id,
tg.name as tag_name
FROM
topics t
JOIN topic_tags tt ON t.id = tt.topic_id
JOIN tags tg ON tg.id = tt.tag_id
WITH 语句和子查询
SQL WITH 语句,也称为公共表表达式(CTE),用于创建可以在另一个 SELECT 语句中引用的临时结果集。这在处理复杂 SQL 查询时特别有用,因为它可以帮助简化查询,使其更易于阅读和维护。
以下是 WITH 语句的基本示例:
WITH post_counts AS (
SELECT user_id, COUNT(*) as post_count
FROM posts
GROUP BY user_id
)
SELECT u.username, post_counts.post_count
FROM users u
JOIN post_counts ON u.id = post_counts.user_id
ORDER BY post_counts.post_count DESC;
在此查询中,WITH 语句创建了一个临时表 post_counts,其中包含每个用户的 ID 及其总帖子数。主查询然后将该表与 users 表连接,以获取与每个 ID 关联的用户名,并按帖子数降序排列结果。
此查询将返回每个用户发布的帖子数量。
子查询是嵌套在另一个查询内部的查询。子查询可以用于 SELECT 语句内部,也可以用于另一个子查询内部。子查询可以返回一组记录、单条记录或单个值。
以下是 子查询 的示例:
SELECT u.username
FROM users u
WHERE u.id IN (
SELECT p.user_id
FROM posts p
GROUP BY p.user_id
HAVING COUNT(p.id) > 100
)
在此示例中,子查询 (SELECT p.user_id FROM posts p GROUP BY p.user_id HAVING COUNT(p.id) > 100) 从 posts 表中选择发布了超过 100 条帖子的用户的 user_id。主查询然后从 users 表中获取这些 user_id 对应的 username。
此查询将返回发布了超过 100 条帖子的用户用户名列表。