这是我收藏的一些相关的题目,其中包含基本操作(如创建表、插入数据)(注意一般企业也不会让删除数据啥的,毕竟刚进去哪会让对人家数据库做什么操作,我是实习的时候参加了数据仓库的建设,插了一些表)、进阶操作(如联结查询、分组聚合、窗口函数、子查询、排序等)以及复杂操作(如计算各种比率、复杂的查询等)。每个实验使用不同的业务场景并针对特定SQL操作进行练习。
数据集设计 !创建以下三张表:
users 表:包含用户的基本信息。 products 表:包含产品信息。 transactions 表:包含用户购买产品的交易记录。
实验 1:基本操作 - 创建表、插入数据和简单查询
步骤: 创建 users, products, 和 transactions 表。 插入表格数据。
简单查询:查询所有用户的姓名、年龄和邮箱。
注意我这个是再jupyter上运行的sql(代码即将上传到github),自己懒得找地方了,所以大家伙凑活着看,我后面会把重要的内容放出来。
# 创建 users 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER NOT NULL,
email TEXT
)
''')
# 创建 products 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_name TEXT NOT NULL,
price REAL NOT NULL
)
''')
# 创建 transactions 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
transaction_date TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(product_id) REFERENCES products(id)
)
''')
# 插入数据到 users 表
users_data = [
('Alice', 25, 'alice@example.com'),
('Bob', 30, 'bob@example.com'),
('Charlie', 35, 'charlie@example.com'),
('David', 40, 'david@example.com'),
('Eve', 29, 'eve@example.com'),
('Frank', 22, 'frank@example.com'),
('Grace', 31, 'grace@example.com'),
('Hannah', 28, 'hannah@example.com'),
('Ivy', 26, 'ivy@example.com'),
('Jack', 45, 'jack@example.com')
]
cursor.executemany("INSERT INTO users (name, age, email) VALUES (?, ?, ?)", users_data)
# 插入数据到 products 表
products_data = [
('Laptop', 1000),
('Smartphone', 500),
('Tablet', 300),
('Headphones', 100),
('Monitor', 200),
('Keyboard', 50),
('Mouse', 25),
('Printer', 150),
('Camera', 800),
('Smartwatch', 200)
]
cursor.executemany("INSERT INTO products (product_name, price) VALUES (?, ?)", products_data)
# 插入数据到 transactions 表
transactions_data = [
(1, 1, 2, '2024-01-15'), # Alice 购买了 2 个 Laptop
(2, 2, 1, '2024-02-20'), # Bob 购买了 1 个 Smartphone
(3, 3, 1, '2024-03-05'), # Charlie 购买了 1 个 Tablet
(4, 4, 4, '2024-04-12'), # David 购买了 4 个 Headphones
(5, 5, 1, '2024-05-25'), # Eve 购买了 1 个 Monitor
(6, 6, 3, '2024-06-15'), # Frank 购买了 3 个 Keyboard
(7, 7, 2, '2024-07-10'), # Grace 购买了 2 个 Mouse
(8, 8, 1, '2024-08-20'), # Hannah 购买了 1 个 Printer
(9, 9, 1, '2024-09-05'), # Ivy 购买了 1 个 Camera
(10, 10, 2, '2024-10-12'), # Jack 购买了 2 个 Smartwatch
(1, 2, 1, '2024-10-15'), # Alice 再次购买了 1 个 Smartphone
(3, 1, 1, '2024-11-20') # Charlie 购买了 1 个 Laptop
]
cursor.executemany("INSERT INTO transactions (user_id, product_id, quantity, transaction_date) VALUES (?, ?, ?, ?)", transactions_data)
conn.commit()
步骤: 创建 users, products, 和 transactions 表。 插入表格数据。
简单查询:查询所有用户的姓名、年龄和邮箱。
SELECT name, age, email FROM users
实验 2:进阶操作 - 联结查询、分组与排序
步骤: 联结 users 和 transactions 表,查询每个用户的购买记录。 联结 transactions 和 products 表,计算每个用户购买的产品总金额。 对用户按总金额进行降序排序。
2.1查询用户的购买记录,
这里用到了join连接表,因为要查购买记录的那内容,需要知道用户名称,和产品名称,和产品自身的内容
SELECT users.name, products.product_name, transactions.quantity, transactions.transaction_date
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
2. 2计算每个用户的总购买金额
这里用到了sum这个函数,进行计算总金额的钱数目,注意这里groupby一定要只对应这个,因为是按照uernname进行分组的,所以必须上面有uername,然后其他的都得是聚合函数了,不然会乱套。
SELECT users.name, SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
2.3. 对用户按总购买金额进行降序排序
这里用到了orderby进行一个排序的操作,进行计算总金额的钱数目
SELECT users.name, SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
ORDER BY total_spent DESC
实验 3:窗口函数 - 计算累计总金额、排名等
步骤: 使用窗口函数,按用户计算每次交易的累计总金额。 计算每个用户的购买排名。
3.1. 计算累计总金额
这里就涉及到开窗打法了,因为我要知道“按用户”所以得按照用户名字来进行一个窗口
我要计算每个用户的累计总消费金额,按照他们购买商品的日期顺序逐步累加金额。
窗口函数:SUM(products.price * transactions.quantity) OVER (PARTITION BY users.id ORDER BY transactions.transaction_date)。
SUM(...):对每个用户累加他们的购买金额(价格 × 数量)。
OVER (PARTITION BY users.id ORDER BY transactions.transaction_date):
定义窗口:
PARTITION BY users.id:按用户ID分组(每个用户单独计算)。
ORDER BY transactions.transaction_date:按照购买日期的顺序累加金额(越早的交易越先累加)。
这就是窗口函数的关键点,它允许在不需要将数据折叠成单行的情况下,逐行计算累积值(类似于带顺序的“滚动”求和)。
SELECT users.name, products.product_name, transactions.quantity,
SUM(products.price * transactions.quantity) OVER (PARTITION BY users.id ORDER BY transactions.transaction_date) AS running_total
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
ORDER BY users.name, transactions.transaction_date
3.2. 计算购买排名
因为我要根据每个用户的总消费金额进行排名。
聚合函数:SUM(products.price * transactions.quantity) 计算每个用户的总消费金额。
窗口函数:RANK() OVER (ORDER BY SUM(...) DESC)。
RANK():计算排名。相同的总消费金额会给出相同的排名值。
OVER (ORDER BY SUM(...) DESC):定义窗口:
ORDER BY SUM(...) DESC:按照总消费金额从高到低排序。
注意:RANK() 与窗口函数结合,生成的结果是用户的排名。总消费金额越高,排名越靠前。
SELECT users.name,
SUM(products.price * transactions.quantity) as total_spent,
RANK() OVER (ORDER BY SUM(products.price * transactions.quantity) DESC) as spending_rank
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
在 SQL 中,使用聚合函数(如 SUM()、AVG()、COUNT() 等)时,理解它们的作用域和计算顺序非常重要。特别是在使用 GROUP BY 子句时,聚合函数计算的结果通常无法在同一查询中直接再使用。
聚合函数的计算顺序
选择数据:首先,从表中选择数据。
应用 WHERE 过滤:过滤数据行,删除不满足条件的行。
执行 GROUP BY:将数据按指定列分组。
应用聚合函数:对每个组计算聚合函数(如 SUM()、AVG() 等)。
应用 HAVING 过滤(可选):在分组后对聚合结果进行过滤。
选择最终结果:选择最终的输出列。
实验 4:高级操作 - 复杂子查询、比率计算
步骤: 使用子查询计算每个用户的平均购买金额。 计算每个用户的购买占比(每个用户的总金额与所有用户总金额的比率)。
4.1. 每个用户的平均购买金额
SELECT name, AVG(total_spent) as avg_spent
FROM (
SELECT users.name, SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
内部查询:
首先,内部查询计算每个用户的总消费金额 (total_spent)。这是通过对每个用户的所有购买记录进行分组并求和得到的。结果会是每个用户的名字和他们的总消费金额。
外部查询:
然后,外部查询从内部查询的结果中获取每个用户的名字和他们的平均消费金额。由于内部查询的结果集已经按用户进行了分组,因此外部查询能够通过 AVG() 函数计算出每个用户的平均消费金额。
4.2. 计算每个用户的购买占比
SELECT name, total_spent,
ROUND(total_spent * 100.0 / (SELECT SUM(total_spent) FROM (
SELECT users.name, SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
)), 2) as percentage
FROM (
SELECT users.name, SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
1、第一个内部查询:
计算每个用户的总消费金额,结果包括每个用户的名字和他们的总消费金额。这个查询会输出每个用户的名字和他们的消费总额。
2、第二个内部子查询:
再次对上述查询进行总结,计算所有用户的总消费金额。这个查询返回的是所有用户的总消费金额,用于计算百分比。
3、外部查询:
从第一个内部查询中获取用户的名字和总消费金额。
计算每个用户的消费占比,即用户的总消费金额占所有用户总消费金额的百分比,并将结果四舍五入到小数点后两位。
实验 5:综合练习 - 多表联结、复杂排序、分组与子查询
步骤: 查找在2024年消费最多的用户。 按每个用户每月的购买金额进行分组统计。
5.1. 查找在2024年消费最多的用户
SELECT users.name, SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
WHERE transactions.transaction_date BETWEEN '2024-01-01' AND '2024-12-31'
GROUP BY users.name
ORDER BY total_spent DESC
LIMIT 1
5.2. 按每个用户每月的购买金额进行分组统计
SELECT users.name, strftime('%Y-%m', transactions.transaction_date) as month,
SUM(products.price * transactions.quantity) as monthly_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name, month
ORDER BY users.name, month
注意这个时间的函数!!!
1. 获取当前日期和时间 | ||
函数 | 说明 | 示例 |
NOW() | 获取当前的日期和时间 | SELECT NOW(); |
CURRENT_TIMESTAMP | 获取当前的日期和时间 | SELECT CURRENT_TIMESTAMP; |
CURDATE() | 获取当前日期 | SELECT CURDATE(); |
CURRENT_DATE | 获取当前日期 | SELECT CURRENT_DATE; |
CURTIME() | 获取当前时间 | SELECT CURTIME(); |
CURRENT_TIME | 获取当前时间 | SELECT CURRENT_TIME; |
2. 日期加减 | ||
函数 | 说明 | 示例 |
DATE_ADD(date, INTERVAL value unit) | 增加时间间隔 | SELECT DATE_ADD('2024-01-01', INTERVAL 10 DAY); |
DATE_SUB(date, INTERVAL value unit) | 减少时间间隔 | SELECT DATE_SUB('2024-01-01', INTERVAL 10 DAY); |
date + interval 'value unit' | 增加时间间隔 | SELECT '2024-01-01'::date + interval '10 days'; |
date - interval 'value unit' | 减少时间间隔 | SELECT '2024-01-01'::date - interval '10 days'; |
`date + value | ' unit'` | |
3. 提取日期部分 | ||
函数 | 说明 | 示例 |
YEAR(date) | 提取年份 | SELECT YEAR('2024-10-21'); |
MONTH(date) | 提取月份 | SELECT MONTH('2024-10-21'); |
DAY(date) | 提取日 | SELECT DAY('2024-10-21'); |
EXTRACT(YEAR FROM date) | 提取年份 | SELECT EXTRACT(YEAR FROM '2024-10-21'); |
EXTRACT(MONTH FROM date) | 提取月份 | SELECT EXTRACT(MONTH FROM '2024-10-21'); |
strftime('%Y', date) | 提取年份 | SELECT strftime('%Y', '2024-10-21'); |
4. 获取星期几 | ||
函数 | 说明 | 示例 |
WEEKDAY(date) | 返回 0-6(0为星期一) | SELECT WEEKDAY('2024-10-21'); |
EXTRACT(DOW FROM date) | 返回 0-6(0为星期日) | SELECT EXTRACT(DOW FROM '2024-10-21'); |
DAYOFWEEK(date) | 返回 1-7(1为星期日) | SELECT DAYOFWEEK('2024-10-21'); |
strftime('%w', date) | 返回 0-6(0为星期日) | SELECT strftime('%w', '2024-10-21'); |
5. 日期和时间格式化 | ||
函数 | 说明 | 示例 |
DATE_FORMAT(date, format) | 格式化日期和时间 | SELECT DATE_FORMAT('2024-10-21', '%Y-%m-%d'); |
TO_CHAR(date, format) | 格式化日期和时间 | SELECT TO_CHAR('2024-10-21'::date, 'YYYY-MM-DD'); |
strftime(format, date) | 格式化日期和时间 | SELECT strftime('%Y-%m-%d', '2024-10-21'); |
6. 日期和时间比较 | ||
函数 | 说明 | 示例 |
直接比较日期 | 使用常规的比较运算符 | SELECT CASE WHEN '2024-01-01' > '2024-01-02' THEN 'Later' ELSE 'Earlier' END AS date_comparison; |
7. Unix 时间戳和时区 | ||
函数 | 说明 | 示例 |
UNIX_TIMESTAMP() | 获取当前的 Unix 时间戳 | SELECT UNIX_TIMESTAMP(); |
EXTRACT(EPOCH FROM timestamp) | 获取 Unix 时间戳(秒) | SELECT EXTRACT(EPOCH FROM NOW()); |
strftime('%s', date) | 获取 Unix 时间戳(秒) | SELECT strftime('%s', '2024-10-21'); |
SET time_zone = 'timezone' | 设置时区 | SET time_zone = 'Asia/Shanghai'; |
以下实验6789是超级综合练习,我觉得会了这几个基本上就无敌了,还能怎么样呢嘿嘿,基本上所有的函数还有开窗大法,子查询,分组什么的应有尽有
实验 6:计算每个用户的最后一次购买日期与购买产品
目标: 使用窗口函数 ROW_NUMBER() 找到每个用户的最后一次购买的产品名称和购买日期。
SELECT name, product_name, transaction_date
FROM (
SELECT users.name, products.product_name, transactions.transaction_date,
ROW_NUMBER() OVER (PARTITION BY users.id ORDER BY transactions.transaction_date DESC) as rn
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
) as t
WHERE t.rn = 1
内层查询 (FROM 子句):
选择字段:
users.name: 用户的名字。
products.product_name: 产品的名称。
transactions.transaction_date: 交易的日期。
窗口函数 (ROW_NUMBER()):
ROW_NUMBER() OVER (PARTITION BY users.id ORDER BY transactions.transaction_date DESC):
PARTITION BY users.id:按照用户的 ID 分组。每个用户的交易将单独进行编号。
ORDER BY transactions.transaction_date DESC:在每个用户的交易中,按交易日期降序排列,最新的交易排在最前面。因为是降雪排列,所以要想要最后一次就是选第一个
结果是每个用户的每个交易都有一个行号 rn,最新的交易的 rn 值为 1。
连接操作 (JOIN):
JOIN transactions ON users.id = transactions.user_id: 连接 users 表和 transactions 表,关联用户和他们的交易。
JOIN products ON transactions.product_id = products.id: 连接 transactions 表和 products 表,关联交易和产品。
外层查询 (SELECT 子句):
从内层查询的结果集中,选择 name、product_name 和 transaction_date 字段。
条件筛选:
WHERE t.rn = 1: 只选择行号为 1 的记录,即每个用户最后一次购买的产品和购买日期。
实验 7:查找购买次数超过2次的用户及其购买次数
目标: 使用 HAVING 和 COUNT() 聚合函数查找购买次数超过2次的用户,并显示他们的购买次数。
SELECT users.name, COUNT(transactions.id) as purchase_count
FROM users
JOIN transactions ON users.id = transactions.user_id
GROUP BY users.name
HAVING COUNT(transactions.id) > 2
实验 8:查找用户的平均购买金额与总购买金额
目标: 计算每个用户的平均购买金额与总购买金额,并按总金额排序。
SELECT users.name,
AVG(products.price * transactions.quantity) as avg_spent,
SUM(products.price * transactions.quantity) as total_spent
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
ORDER BY total_spent DESC
实验 9:查找每个用户购买金额占总购买金额的百分比
目标: 计算每个用户的购买金额占所有用户总购买金额的百分比,并按百分比降序排列。
SELECT users.name,
SUM(products.price * transactions.quantity) as total_spent,
ROUND(SUM(products.price * transactions.quantity) * 100.0 /
(SELECT SUM(products.price * transactions.quantity) FROM transactions JOIN products ON transactions.product_id = products.id), 2) as percentage
FROM users
JOIN transactions ON users.id = transactions.user_id
JOIN products ON transactions.product_id = products.id
GROUP BY users.name
ORDER BY percentage DESC