欢迎来到交互式 SQL 教程!您无需安装任何软件,所有操作都在浏览器中完成。

我们为您预加载了三个示例表:employees, products, 和 orders

表结构 (带中文注释):

  • employees (员工表)
    • id - 员工ID
    • name - 姓名
    • department - 所在部门
    • salary - 薪水
    • hire_date - 入职日期
  • products (商品表)
    • id - 商品ID
    • name - 商品名称
    • category - 类别
    • price - 价格
    • stock - 库存数量
  • orders (订单表)
    • order_id - 订单ID
    • product_id - 关联的商品ID
    • employee_id - 关联的员工ID (销售员)
    • quantity - 订购数量
    • order_date - 下单日期

点击“运行”来执行 SQL,或“重置数据库”恢复初始数据。

本节介绍 SQL 中用于检索和操作数据的基本子句。

1. ORDER BY:排序结果集

ORDER BY 子句用于对 SELECT 语句返回的结果集进行排序。

  • 语法ORDER BY 列1 [ASC|DESC], 列2 [ASC|DESC], ...
  • ASC (Ascending) 表示升序,是默认的排序方式。
  • DESC (Descending) 表示降序,会以相反的顺序排序。
  • 您可以根据多个列进行排序,排序优先级从左到右。

示例SELECT name, salary FROM employees ORDER BY salary DESC;

解释:此查询从 employees(员工)表中选择 name(姓名)和 salary(薪水)两列。ORDER BY salary DESC 子句会根据 salary 列的值进行降序排序,这意味着薪水较高的员工会首先显示。这有助于快速找出薪水最高的员工。


2. LIMITOFFSET:分页显示

LIMITOFFSET 子句通常一起使用,用于实现分页功能,允许您从结果集的特定起始点检索指定数量的行。

  • LIMIT 数量:限制返回的行数。
  • OFFSET 起始点:跳过指定数量的行,从跳过后的位置开始检索。
  • 它们常用于网页分页显示数据,例如“显示第2页,每页10条记录”。

示例1SELECT * FROM products ORDER BY price DESC LIMIT 3;

解释:此查询从 products(商品)表中选择所有列。它首先根据 price(价格)列进行降序排序(最贵的产品在前)。然后,LIMIT 3 将输出限制为仅显示价格最高的3件产品。这对于查找最昂贵的商品非常有用。

示例2SELECT * FROM products ORDER BY price DESC LIMIT 3 OFFSET 3;

解释:与上一个示例类似,此查询也按价格降序排序产品。但是,OFFSET 3 告诉数据库跳过前3行(即跳过最贵的3件产品)。然后,LIMIT 3 检索接下来的3行。这实际上为您提供了第4、5、6件最昂贵的产品,展示了如何获取结果的“下一页”。


3. AS (别名):为列或表指定临时名称

AS 关键字用于在查询中为列或表指定一个临时的名称(别名)。这可以使查询结果更具可读性,并能简化复杂的查询。

  • 列别名语法SELECT 列名 AS 别名 FROM 表名;
  • 表别名语法SELECT 表别名.列名 FROM 表名 AS 表别名; (表别名常用于多表连接中,以缩短表名并避免歧义)。

示例SELECT name AS employee_name, salary AS monthly_salary FROM employees;

解释:此查询从 employees 表中选择 namesalary 列。name AS employee_name 将输出中的 name 列重命名为 employee_namesalary AS monthly_salarysalary 重命名为 monthly_salary。原始表中的列名保持不变;此别名仅对当前查询的结果集有效;这提高了输出的清晰度,尤其当列名模糊或过短时。

本节介绍聚合函数以及 GROUP BYHAVING 子句,它们对于汇总和分析数据至关重要。

1. 聚合函数

聚合函数对一组行执行计算,并返回一个单一的汇总值。常见的聚合函数包括:

  • COUNT():计算行数。
  • SUM():计算数值列的总和。
  • AVG():计算数值列的平均值。
  • MIN():查找列中的最小值。
  • MAX():查找列中的最大值。

示例1SELECT COUNT(*) AS total_employees FROM employees;

解释:此查询使用 COUNT(*) 来计算 employees(员工)表中的所有行,从而得到员工的总数。AS total_employees 为结果列分配了一个更具描述性的别名。

示例2SELECT AVG(salary) AS avg_salary FROM employees;

解释:此查询使用 AVG(salary) 计算所有员工 salary(薪水)列的平均值。结果被别名为 avg_salary


2. GROUP BY:对行进行分组

GROUP BY 子句将指定列中具有相同值的行分组为汇总行。它几乎总是与聚合函数一起使用。当您使用聚合函数时,SELECT 语句中任何未聚合的列都必须包含在 GROUP BY 子句中。

示例SELECT department, COUNT(*) AS num_employees FROM employees GROUP BY department;

解释:此查询根据 department(部门)对 employees 表进行分组。对于每个唯一的部门,COUNT(*) 计算该部门中的员工数量。结果显示每个部门及其员工数量。这有助于了解员工在不同部门的分布情况。


3. HAVING:过滤分组后的结果

HAVING 子句用于过滤 GROUP BY 子句的结果。WHERE 子句在分组之前过滤单个行,而 HAVING 子句在聚合之后过滤分组。

示例SELECT department, AVG(salary) AS avg_salary FROM employees GROUP BY department HAVING AVG(salary) > 80000;

解释:此查询首先按 department 对员工进行分组,并计算每个部门的平均 salary。然后,HAVING AVG(salary) > 80000 子句过滤这些分组,只显示平均薪水大于80,000的部门。这使您能够识别高薪部门。

关系型数据库通常将相关数据存储在多个表中,以减少冗余并提高数据完整性。连接(Joins)用于根据表之间的相关列来组合来自两个或更多表的行。

1. INNER JOIN:内连接

INNER JOIN 只返回两个表中都存在匹配值的行。如果一个表中的行在另一个表中没有匹配的行,则该行将从结果中排除。

示例SELECT e.name AS employee, p.name AS product FROM orders o JOIN employees e ON o.employee_id = e.id JOIN products p ON o.product_id = p.id;

解释:此查询结合了三个表的数据:orders(别名为 o)、employees(别名为 e)和 products(别名为 p)。

  • JOIN employees e ON o.employee_id = e.id:这部分根据 orders 表中的 employee_idemployees 表中的 id 匹配来连接 ordersemployees 表。这会将每个订单与其对应的员工关联起来。
  • JOIN products p ON o.product_id = p.id:这部分接着将结果与 products 表连接,根据 orders 表中的 product_idproducts 表中的 id 匹配。这会将每个订单与其订购的产品关联起来。

SELECT 语句然后检索每个匹配订单的员工姓名和产品名称。结果显示“谁订购了什么”。


2. LEFT JOIN (或 LEFT OUTER JOIN):左连接

LEFT JOIN 返回左表中的所有行,以及右表中匹配的行。如果右表中没有匹配的行,则右表列的值将返回 NULL

示例SELECT e.name, o.order_id FROM employees e LEFT JOIN orders o ON e.id = o.employee_id;

解释:此查询将 employees 表(左表,别名为 e)与 orders 表(右表,别名为 o)结合。

  • ON e.id = o.employee_id:连接条件根据 employee_id 匹配员工和他们的订单。

LEFT JOIN 确保所有员工都包含在结果中,即使他们没有下过任何订单。对于没有匹配订单的员工,orders 表中的 order_id 列将显示 NULL。此查询有效地显示了“所有员工及其订单”,包括那些没有订单的员工。

本节深入探讨更复杂的查询技术,这些技术能够实现强大的数据操作和分析。

1. 子查询 (Subquery):嵌套查询

子查询是嵌套在另一个 SQL 查询内部的查询。子查询可以在 SELECTFROMWHEREHAVING 子句中使用。它们通常用于返回单个值或一组值,然后供外部查询使用。

示例SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);

解释:此查询使用子查询 (SELECT AVG(salary) FROM employees) 首先计算所有员工的平均薪水。外部查询然后选择 salary 大于此计算出的平均薪水的员工的 name(姓名)和 salary(薪水)。这有效地找出了“高于平均薪水”的员工。


2. CASE 语句:条件逻辑

CASE 语句是一种条件表达式,允许您根据不同的条件定义不同的输出。它类似于编程中的 if-then-else 逻辑。

  • 语法
    CASE
        WHEN condition1 THEN result1
        WHEN condition2 THEN result2
        ...
        ELSE resultN
    END

示例SELECT name, salary, CASE WHEN salary > 100000 THEN '高级' WHEN salary > 70000 THEN '中级' ELSE '初级' END AS salary_level FROM employees;

解释:此查询根据员工的 salary(薪水)为每位员工分配一个“薪水评级”。

  • 如果 salary 大于 100,000,则 salary_level 为 '高级'。
  • 如果 salary 大于 70,000(但不大于 100,000),则为 '中级'。
  • 否则,为 '初级'。

这展示了如何在单个查询中根据多个条件对数据进行分类。

本节涵盖了用于修改表中数据的基本 SQL 命令:UPDATEDELETE。这些是数据操作语言(DML)的一部分。

1. UPDATE:修改现有记录

UPDATE 用于修改表中的现有记录。您需要指定要更新的表、要更改的列以及(可选地)使用 WHERE 子句来指定受影响的行。如果没有提供 WHERE 子句,表中的所有行都将被更新。

  • 语法UPDATE 表名 SET 列1 = 值1, 列2 = 值2, ... WHERE 条件;

示例UPDATE products SET price = 28.00 WHERE name = '鼠标';

解释:此查询更新 products(商品)表。它将名为“鼠标”的产品价格设置为 28.00。WHERE name = '鼠标' 子句至关重要,因为它确保只更改特定产品的价格,而不是所有产品。运行此查询后,您可以执行 SELECT * FROM products; 来查看变化。


2. DELETE:删除记录

DELETE 用于从表中删除现有记录。与 UPDATE 类似,WHERE 子句用于指定要删除的行。如果没有提供 WHERE 子句,表中的所有行都将被删除,从而清空该表。

  • 语法DELETE FROM 表名 WHERE 条件;

示例DELETE FROM orders WHERE order_id = 3;

解释:此查询从 orders(订单)表中删除一条记录。WHERE order_id = 3 子句确保只删除 order_id 等于 3 的订单。

重要提示DELETE 操作是不可逆的。一旦数据被删除,除非您有备份或在可以回滚的事务中,否则数据将丢失。教程中正确地警告:“注意: 操作无法撤销!可点击“重置数据库”恢复。”

本节涵盖了修改现有表结构的操作以及如何检查表模式。

1. ALTER TABLE:修改表结构

ALTER TABLE 用于在现有表中添加、删除或修改列。它还可以用于在现有表上添加和删除各种约束。

  • 添加列语法ALTER TABLE 表名 ADD COLUMN 列名 数据类型 [约束];

示例ALTER TABLE employees ADD COLUMN status TEXT DEFAULT 'Active';

解释:此查询通过添加一个名为 status 的新列来修改 employees(员工)表。TEXT 数据类型表示它将存储文本,DEFAULT 'Active' 为插入到表中的任何新行以及现有行(取决于具体的 SQL 方言,SQLite 会将现有行设置为默认值)设置默认值为 'Active'。

运行后可执行 SELECT * FROM employees; 查看变化。


2. 查询表结构:使用 PRAGMA table_info()

虽然 SELECT 用于检索数据,但您通常需要了解表的结构(模式),包括其列、数据类型和约束。在 SQLite(本教程通过 sql.js 使用)中,您可以使用 PRAGMA table_info()

  • 语法PRAGMA table_info(表名);

示例PRAGMA table_info('employees');

解释:此查询使用 SQLite 特有的 PRAGMA table_info() 命令来检索 employees 表结构的详细信息。结果将显示每列的名称、数据类型、是否可以为 NULL、其主键状态和默认值。这对于理解表的模式非常有用。

本节介绍更高级的 SQL 功能,这些功能能够实现复杂的数据分析和操作,通常可以简化原本非常复杂的查询。

1. 公用表表达式 (CTE - Common Table Expression):使用 WITH 语句

CTE 使用 WITH 子句定义,是一个临时的、命名的结果集,您可以在单个 SELECTINSERTUPDATEDELETE 语句中引用它。CTE 提高了复杂查询的可读性和可维护性,尤其是在处理递归查询或需要多次引用相同子查询时。

  • 语法
    WITH cte_name (列1, 列2, ...) AS (
        -- CTE 查询定义
        SELECT ...
    )
    -- 使用 CTE 的主查询
    SELECT ... FROM cte_name WHERE ...;

示例

WITH HighSalaryEngineers AS (
  SELECT id, name, salary
  FROM employees
  WHERE department = '工程部' AND salary > 100000
)
SELECT * FROM HighSalaryEngineers;

解释:此查询使用名为 HighSalaryEngineers 的 CTE,首先找出在“工程部”且 salary(薪水)大于 100,000 的员工。然后,主查询 SELECT * FROM HighSalaryEngineers; 从这个临时结果集中检索所有列。这通过将复杂的条件分解为可读的、命名的子查询,从而轻松地“查找高薪工程师”。


2. 窗口函数 (Window Functions):在相关行上执行计算

窗口函数对一组与当前行相关的表行执行计算。与聚合函数(如 SUMCOUNT)不同,窗口函数不会将行折叠成单个输出行;相反,它们为结果集中的每一行返回一个值。它们通常用于排名、移动平均或累积和。

  • 语法函数名(表达式) OVER (PARTITION BY 列1 ORDER BY 列2 [ASC|DESC])
  • PARTITION BY:将行分成组(分区)以供窗口函数独立应用。
  • ORDER BY:对每个分区内的行进行排序。

示例

SELECT
  name,
  department,
  salary,
  RANK() OVER (PARTITION BY department ORDER BY salary DESC) as rank_in_department
FROM employees;

解释:此查询使用 RANK() 窗口函数,根据每个员工在其所属部门内的 salary(薪水)为其分配排名。

  • PARTITION BY department:将员工按部门分成不同的组。
  • ORDER BY salary DESC:在每个部门内部,员工按 salary 降序排序。

RANK() 然后根据此排序顺序为部门内的每个员工分配排名。如果两个员工的薪水相同,他们将获得相同的排名,并且下一个排名将被跳过(例如,1、2、2、4)。这使您能够“按部门薪水排名”。

以下是更多可供尝试的 SQL 查询示例:

SQL 编辑器
结果
点击“运行”来查看查询结果。