欢迎来到交互式 SQL 教程!您无需安装任何软件,所有操作都在浏览器中完成。
我们为您预加载了三个示例表:employees
, products
, 和 orders
。
表结构 (带中文注释):
id
- 员工IDname
- 姓名department
- 所在部门salary
- 薪水hire_date
- 入职日期id
- 商品IDname
- 商品名称category
- 类别price
- 价格stock
- 库存数量order_id
- 订单IDproduct_id
- 关联的商品IDemployee_id
- 关联的员工ID (销售员)quantity
- 订购数量order_date
- 下单日期点击“运行”来执行 SQL,或“重置数据库”恢复初始数据。
本节介绍 SQL 中用于检索和操作数据的基本子句。
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
列的值进行降序排序,这意味着薪水较高的员工会首先显示。这有助于快速找出薪水最高的员工。
LIMIT
和 OFFSET
:分页显示LIMIT
和 OFFSET
子句通常一起使用,用于实现分页功能,允许您从结果集的特定起始点检索指定数量的行。
LIMIT 数量
:限制返回的行数。OFFSET 起始点
:跳过指定数量的行,从跳过后的位置开始检索。示例1:SELECT * FROM products ORDER BY price DESC LIMIT 3;
解释:此查询从 products
(商品)表中选择所有列。它首先根据 price
(价格)列进行降序排序(最贵的产品在前)。然后,LIMIT 3
将输出限制为仅显示价格最高的3件产品。这对于查找最昂贵的商品非常有用。
示例2:SELECT * FROM products ORDER BY price DESC LIMIT 3 OFFSET 3;
解释:与上一个示例类似,此查询也按价格降序排序产品。但是,OFFSET 3
告诉数据库跳过前3行(即跳过最贵的3件产品)。然后,LIMIT 3
检索接下来的3行。这实际上为您提供了第4、5、6件最昂贵的产品,展示了如何获取结果的“下一页”。
AS
(别名):为列或表指定临时名称AS
关键字用于在查询中为列或表指定一个临时的名称(别名)。这可以使查询结果更具可读性,并能简化复杂的查询。
SELECT 列名 AS 别名 FROM 表名;
SELECT 表别名.列名 FROM 表名 AS 表别名;
(表别名常用于多表连接中,以缩短表名并避免歧义)。示例:SELECT name AS employee_name, salary AS monthly_salary FROM employees;
解释:此查询从 employees
表中选择 name
和 salary
列。name AS employee_name
将输出中的 name
列重命名为 employee_name
,salary AS monthly_salary
将 salary
重命名为 monthly_salary
。原始表中的列名保持不变;此别名仅对当前查询的结果集有效;这提高了输出的清晰度,尤其当列名模糊或过短时。
本节介绍聚合函数以及 GROUP BY
和 HAVING
子句,它们对于汇总和分析数据至关重要。
聚合函数对一组行执行计算,并返回一个单一的汇总值。常见的聚合函数包括:
COUNT()
:计算行数。SUM()
:计算数值列的总和。AVG()
:计算数值列的平均值。MIN()
:查找列中的最小值。MAX()
:查找列中的最大值。示例1:SELECT COUNT(*) AS total_employees FROM employees;
解释:此查询使用 COUNT(*)
来计算 employees
(员工)表中的所有行,从而得到员工的总数。AS total_employees
为结果列分配了一个更具描述性的别名。
示例2:SELECT AVG(salary) AS avg_salary FROM employees;
解释:此查询使用 AVG(salary)
计算所有员工 salary
(薪水)列的平均值。结果被别名为 avg_salary
。
GROUP BY
:对行进行分组GROUP BY
子句将指定列中具有相同值的行分组为汇总行。它几乎总是与聚合函数一起使用。当您使用聚合函数时,SELECT
语句中任何未聚合的列都必须包含在 GROUP BY
子句中。
示例:SELECT department, COUNT(*) AS num_employees FROM employees GROUP BY department;
解释:此查询根据 department
(部门)对 employees
表进行分组。对于每个唯一的部门,COUNT(*)
计算该部门中的员工数量。结果显示每个部门及其员工数量。这有助于了解员工在不同部门的分布情况。
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)用于根据表之间的相关列来组合来自两个或更多表的行。
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_id
与 employees
表中的 id
匹配来连接 orders
和 employees
表。这会将每个订单与其对应的员工关联起来。JOIN products p ON o.product_id = p.id
:这部分接着将结果与 products
表连接,根据 orders
表中的 product_id
与 products
表中的 id
匹配。这会将每个订单与其订购的产品关联起来。SELECT
语句然后检索每个匹配订单的员工姓名和产品名称。结果显示“谁订购了什么”。
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
。此查询有效地显示了“所有员工及其订单”,包括那些没有订单的员工。
本节深入探讨更复杂的查询技术,这些技术能够实现强大的数据操作和分析。
子查询是嵌套在另一个 SQL 查询内部的查询。子查询可以在 SELECT
、FROM
、WHERE
或 HAVING
子句中使用。它们通常用于返回单个值或一组值,然后供外部查询使用。
示例:SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
解释:此查询使用子查询 (SELECT AVG(salary) FROM employees)
首先计算所有员工的平均薪水。外部查询然后选择 salary
大于此计算出的平均薪水的员工的 name
(姓名)和 salary
(薪水)。这有效地找出了“高于平均薪水”的员工。
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 命令:UPDATE
和 DELETE
。这些是数据操作语言(DML)的一部分。
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;
来查看变化。
DELETE
:删除记录DELETE
用于从表中删除现有记录。与 UPDATE
类似,WHERE
子句用于指定要删除的行。如果没有提供 WHERE
子句,表中的所有行都将被删除,从而清空该表。
DELETE FROM 表名 WHERE 条件;
示例:DELETE FROM orders WHERE order_id = 3;
解释:此查询从 orders
(订单)表中删除一条记录。WHERE order_id = 3
子句确保只删除 order_id
等于 3 的订单。
重要提示:DELETE
操作是不可逆的。一旦数据被删除,除非您有备份或在可以回滚的事务中,否则数据将丢失。教程中正确地警告:“注意: 操作无法撤销!可点击“重置数据库”恢复。”
本节涵盖了修改现有表结构的操作以及如何检查表模式。
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;
查看变化。
PRAGMA table_info()
虽然 SELECT
用于检索数据,但您通常需要了解表的结构(模式),包括其列、数据类型和约束。在 SQLite(本教程通过 sql.js
使用)中,您可以使用 PRAGMA table_info()
。
PRAGMA table_info(表名);
示例:PRAGMA table_info('employees');
解释:此查询使用 SQLite 特有的 PRAGMA table_info()
命令来检索 employees
表结构的详细信息。结果将显示每列的名称、数据类型、是否可以为 NULL、其主键状态和默认值。这对于理解表的模式非常有用。
本节介绍更高级的 SQL 功能,这些功能能够实现复杂的数据分析和操作,通常可以简化原本非常复杂的查询。
WITH
语句CTE 使用 WITH
子句定义,是一个临时的、命名的结果集,您可以在单个 SELECT
、INSERT
、UPDATE
或 DELETE
语句中引用它。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;
从这个临时结果集中检索所有列。这通过将复杂的条件分解为可读的、命名的子查询,从而轻松地“查找高薪工程师”。
窗口函数对一组与当前行相关的表行执行计算。与聚合函数(如 SUM
或 COUNT
)不同,窗口函数不会将行折叠成单个输出行;相反,它们为结果集中的每一行返回一个值。它们通常用于排名、移动平均或累积和。
函数名(表达式) 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 查询示例: