你现在看到这篇博客,可能是因为你急于回忆起遗忘多时的SQL. SQL比编程要简单地多,因此回忆起来也是相当简单。
为了回忆SQL,我需要下载MySQL或其它数据库吗?当然可以。不过点开这篇博客的家人们有福了,如果只是为了回忆SQL的基本操作,直接使用Python内置的sqlite3即可。运行以下程序即可马上体验SQL的魅力:
import os, sqlite3def run_sql(temp_file:str = 'temp.sqlite3'):con = sqlite3.connect(temp_file)cur = con.cursor()while True:sql = input('> ')if sql == 'exit':con.close()os.remove(temp_file)breaktry:result = cur.execute(sql)if 'select' in sql.lower():for item in result.fetchall():print(item)elif 'insert' in sql.lower() or 'drop' in sql.lower():con.commit()except Exception as e:print(e)if __name__ == '__main__':run_sql()
不过,sqlite3对数据类型的支持没有主流数据库那么好,但这对文中内容毫无影响。另外,SQL语句的关键字是不区分大小写的。
众所周知,一位自称是“数据科学工作者”的人,他的工作可能就是写SQL. 传统SQL讲究四门工夫——“增删查改”。“增”可以创建表、向表中写入数据;“删”可以删表、删数据,有一种传统艺能叫作“删库跑路”;“查”是SQL的重中之重,大部分SQL都是用于查找的;“改”可以修改表中数据,这个并不常见,在文中一笔带过。
学过数据库的朋友都知道,SQL有两种老掉牙的例子:一是学生、二是订单,我们这里使用的例子正是学生。
“增”:创建表、向表中添加行
建表语句如下:
CREATE TABLE stu (id INTEGER PRIMARY KEY, name VARCHAR(32), age INTEGER);
创建了表stu,包含3列:id(学号)、name(姓名)、age(年龄)。
向表中添加记录(10001, 张萌萌, 12), 插入语句如下:
INSERT INTO stu VALUES(10001, '张萌萌', 12);
我们再多插入几条记录:
INSERT INTO stu VALUES(10002, '李帅', 13);
INSERT INTO stu VALUES(10003, '王杰', 11);
INSERT INTO stu VALUES(10004, '欧阳清华', 14);
“查”:查找表中内容
简单查找
查找语句应该是大家记忆最深的语句了,其格式为:
SELECT [列表项] FROM [表名] WHERE [筛选条件];
例如,我们要查看表stu中的所有行:
select * from stu;
查找结果为:
(10001, '张萌萌', 12)
(10002, '李帅', 13)
(10003, '王杰', 11)
(10004, '欧阳清华', 14)
如果我们要查找年龄大于12的所有学生姓名:
SELECT name FROM stu WHERE age > 12;
查找结果为:
('李帅',)
('欧阳清华',)
数据聚合
SQL中常用的内置聚合函数有COUNT、MIN、MAX、SUM
和AVG
,它们用于执行简单的统计功能。例如,计算表stu中学生的平均年龄:
SELECT AVG(age) FROM stu;
关于COUNT
COUNT(*)
可统计表的行数,而COUNT(age)
可以统计列age中,非空(NOT NULL)值的个数。
分组查询
可通过GROUP BY
将表分组,再对每个分组运用统计函数。为了演示效果,我们在表stu中再添加几条记录:
INSERT INTO stu VALUES(10005, '李芳芳', 13);
INSERT INTO stu VALUES(10006, '王菜刀', 11);
INSERT INTO stu VALUES(10007, '诸葛韭菜', 11);
插入后,表stu中有年龄重复的行了。如果我们要统计表中各个年龄的有多少人,可以首先根据年龄,将表中数据分为若干个组,再利用统计函数COUNT
,统计每个分组的行数:
SELECT age, COUNT(*) FROM stu GROUP BY age;
输出为:
(11, 3)
(12, 1)
(13, 2)
(14, 1)
提示
当查询语句同时包含
WHERE
和GROUP BY
时,WHERE
先执行,GROUP BY
作用于WHERE
过滤后的表上。GROUP BY
的表可通过HAVING
进一步筛选,但用得比较少,一般在WHERE中写好过滤条件。
结果排序
对表排序是最常见的一种需求,只需要通过ORDER BY指定排序的列即可。例如,将表stu按照成绩从高到低排序:
SELECT * FROM stu ORDER BY age DESC;
SQL默认从小到大排序,想要从大到小排序,需要指定关键字DESC
. 排序时可以指定多个列名。
“删”:删除表中特定行、删除表
删除表中特定行的SQL语句为:
DELETE FROM [表名] WHERE [指定条件];
删除表的操作为:
DROP TABLE [表名];
现在,请删除表stu,我们将重新生成其它表,温习更复杂的查询操作。
“查”:表的连接操作
两张列相同的表可以执行UNION、INTERSECT等集合操作,例如:
(SELECT id, name FROM stu1) UNION (SELECT id, name FROM stu2);
下面重点考察内外连接、左右链接。
有如下两张表:
id | name |
10001 | 王一 |
10002 | 王二 |
10003 | 王三 |
id | age |
10001 | 11 |
10002 | 12 |
10004 | 14 |
10005 | 12 |
将表中具有相同id的行连接起来。
SELECT stu_name.id, name, age FROM stu_name INNER JOIN stu_age ON stu_name.id = stu_age.id;
/*
(10001, '王一', 11)
(10002, '王二', 12)
*/SELECT stu_name.id, name, age FROM stu_name LEFT JOIN stu_age ON stu_name.id = stu_age.id;
/*
(10001, '王一', 11)
(10002, '王二', 12)
(10003, '王三', None)
*/SELECT stu_name.id, name, age FROM stu_name RIGHT JOIN stu_age ON stu_name.id = stu_age.id;
/*
(10001, '王一', 11)
(10002, '王二', 12)
(None, None, 14)
(None, None, 12)
*/
由此可见这几种查询模式的区别:当两侧数据都有效时,才能内连接上;左连接保证左边表的指定列中数据都在表中,而右链接则是保证右边的。