SQL语言入门
2.1 从Excel迈向SQL
Excel | SQL |
---|---|
处理数据是存储在一个工作簿中 | 数据存储在数据库中 |
由若干个工作表组成 | 由若干个数据表组成 |
SQL —— Structured Query Language ”结构化查询语言“的简称,是目前对数据库进行查询和编辑的主流编程语言
SQL依靠服务器,计算资源远超过Excel,在具体的分析场景中,运行速度和代码可读性非常重要
例如,筛选数据:想要找出单价大于2块钱的蔬菜类产品的数据。
在excel中,需要先判断数据是否已经有选择过筛选器,又因为涉及到两个变量的筛选,需要首先选择产品类型为蔬菜的类型,再选择单价大于2。
在SQL语言中,where product_type = 蔬菜 and price > 2,就可以在结果框中看到结果。
再例如,对数据分组后的汇总计算:计算不同产品的交易次数,总交易金额以及平均单价。
在excel中有两种处理方式:
- 使用数据透视表功能
- 虽然数据透视表很强大,但是需要增加新的工作表,使数据管理工作更加复杂
- 在单元格中写繁琐的公式
- (COUNT,COUNTA,COUNTIF,COUNTIFS,COUNTBLANK)书写复杂,也不易被阅读
在SQL中,只需要用简单的count()、sum()和group by语句就可以解决了。
使用SQL语言后,分析工作的流程和思路都保存在单独的文件中,在团队内部可以分享和探讨。
SQL可以很轻松的从旧的数据迁移到新的数据中,并验证分析结果是否仍然在新数据中依旧成立。
2.2 用SQL认识和理解数据
数据需求 | SQL语言 |
---|---|
抽取(筛选)/数据查询 | SELECT…FROM…WHERE |
拼接/连接各种数据表 | JOIN |
聚合/数学运算 | GROUP BY |
DESCRIBE语句
功能:描述指定表或视图中的所有列,帮助了解数据表中包含了哪些列。
用法:DESCRIBE Table_name;
为了方便数据库的管理,通常数据库中每个表都设定主键,主键是各个数据表之间连接的桥梁。
从业务角度来理解,交易id类似于订单号,顾客id是指每个购买了产品的会员id,由于一个顾客可能购买多次,所以在销售表中可能重复。
SQL的数据类型
可能存储的格式 | 数据示例 | |
---|---|---|
字符 | char,vchar,text,string | 人名,地址 |
时间日期 | Timestamp,date,hour | 销售日期 |
数字 | Integer,bigint | 价格,销量 |
好的数据分析习惯:将DESCRIBE表的结果存储成文档,这类文档通常称为“数据字典”,是对数据中的结构,数据类型和存储逻辑进行定义的文档。数据字典可以告诉我们这个表有哪些字段?数据类型是什么?业务意义是什么?
对于存储了大量表格的数据,一个个去DESCRIBE会显得效率很低。所以我们应该尽早就建立便于查阅的“数据字典”。
SELECT语句
功能:查询表,帮助知道表的一个大致结构(表里包含哪些列,有多少条记录数等)
基本格式:SELECT 字段名 FROM 数据表;
这里的分号是表示一个SQL语句结束的标记
SELECT * FROM Sales;
-- 这里的*代表数据表中包含的所有列变量
由于我们接触到的数据表往往包含几百万甚至千万行的数据
LIMIT语句
功能:SQL中的限制语句,用来控制显示结果的行数
SELECT * FROM Sales limit 5;
-- 这里只会显示前5行
DISTINCT语句
功能:去掉重复的取值或记录
SELECT DISTINCT(Product_ID) FROM Sales;
COUNT函数
SQL语句中用来统计个数的函数
功能:查询满足条件的记录总数,也就是行数
SELECT COUNT(Member_ID) FROM Sales; -- 发生交易的记录数(会去掉NULL值)那么如果COUNT(Member_ID)和COUNT(*)的结果不一样,就说明Member_ID有缺失值
SELECT COUNT(DISTINCT Member_ID) FROM Sales; -- 有多少个顾客
SELECT COUNT(*) FROM Sales; --整个销售表有多少行数据
min/max/avg/sum函数 (最小值最大值平均值求和)
最好的了解数字变量的方式就是分析其最小值、最大值和平均值这些基本的描述性统计指标。
用法:函数(目标字段)
SELECT min(age),max(age),avg(age),sum(age) FROM Sales;
-- 这里avg(age) = sum(age)/count(age)
max()和min()也可以用于查找日期的远近。当然也可以用于字符类型中,通常数据库会默认但也英文字符中,越接近a则认为越小,越接近z则认为是越大,但是计算字符大小没有什么意义。
2.3 用SQL筛选数据
数据量的剧增使我们在分析中必须要进行筛选,以聚焦特定的分析对象。
比如对销售趋势的分析锁定在某个特定区域或者特定产品上,这样可以减少计算的数据集大小,提升SQL语言的运行速度,方便我们验证对数据的理解。
WHERE语句
WHERE语句是SQL语句中的筛选语句
功能:对数据按照特定的条件去进行筛选
一般而言,WHERE会在FROM的后面
-- 选出注册城市在上海的会员
SELECT * FROM MEMBER WHERE CITY = '上海';
大于小于等于
等号 | = |
---|---|
不等于 | <>或!= |
大于等于 | >= |
小于等于 | <= |
IS NULL和IS NOT NULL
SELECT * FROM MEMBER WHERE CITY IS NOT NULL;
IN/BETWWN/LIKE
**IN:**让SQL能返回满足某个变量部分指定值的结果。判断目标字段是否是某几个数字或者字符中的一个。
格式:IN(值1,值2,值3…)
-- 找出在武汉,成都或者上海购物过的顾客:
SELECT * FROM Sales WHERE CITY in('武汉','上海','成都');
IN是是等于号的增强版。
WHERE CITY = '武汉'和 CITY IN(武汉) 的结果是一样的
**BETWEEN:**任何在两个取值范围之间的记录(包含界限两边的值,是个左闭右闭的区间)
格式:BETWEEN 值1 AND 值2
-- 找到单价在20-80之间的商品
SELECT * FROM Product WHERE price BETWEEN 20 AND 80;
**LIKE:**更宽泛的匹配模式
等于/不等于:精确匹配
IN/BETWEEN:范围匹配
LIKE:模糊匹配
使用LIKE操作符时,有个非常有用的搭档——作为通配符出现的百分号符%,可以代替任何长度的字符。
-- 查找所有姓张的客户
SELECT * FROM member WHERE 'name' LIKE '张%';
AND和OR操作符
AND:”和“,筛选出来的数据同时满足AND两边条件的数据
OR:“或”,筛选出来的数据只要满足OR两边的任意一个条件
LEFT和RIGHT
SQL语句中的LEFT函数和RIGHT函数可以取出某个字符串中特定长度的字符
-- 例如注册日期是用了字符串的方式保存的,类似'2020-01-05 09:30:27'
SELECT LEFT(r_date,10),RIGHT(r_date,8) FROM members;
-- 这里LEFT就取出了从第一个字符开始从左往右连续10个字符,RIGHT则取出了从最后一个字符开始从右往左的8个字符
AS语句
功能:避免两个字段的混淆,可以用AS操作符对变量进行命名或者重命名
-- 如销售表里的交易城市和会员表里的注册城市都是city则容易被搞混
SELECT city AS new_city FROM Sales;
但是这一步知识更改了结果显示窗口的命名,如果要将new_city这个别名长期保留下来,则要创建新表。
前面的avg()函数和sum()函数等运算都是垂直方向(列方向)的运算,在SQL语句中也可以进行水平方向(行方向)运算。
-- 每个交易的单件产品均价 = 交易金额/交易件数
SELECT Product_ID,Amount,Quant,Amount/Quant AS Price FROM Sales;
2.4 用SQL回答业务问题(连接问题)
为了实现表与表之间的匹配连接,需要使用到SQL语句的连接语句JOIN语句,JOIN语句可以将表和表之间进行连接,通常将JOIN语句放到FROM语句的后面。
INNER JOIN:内连接,两个圆圈相叠的部分。
如果我们使用INNER JOIN语句去匹配连接两张表的话,最后得到的显示结果是同时存在于两张表的部分。
SELECT S.product_id,S.amount,P.Base_Category,P.Category
FROM sales AS S INNER JOIN product AS P
ON S.product_id = P.Product_ID;
ON后面是为了告诉SQL,是根据两张表里的哪个字段去进行表与表之间的连接的。
LEFT JOIN:最后的显示结果包含LEFT JOIN语句左边表的全部记录以及语句右边表能匹配上的部分记录。
RIGHT JOIN:最后的显示结果包含RIGHT JOIN语句右边表的全部记录以及语句左边表能匹配上的部分记录。
OUTE JOIN:显示结果是inner join加上left join再加上right join三种join方式的结合。
什么时候会用到outer join呢?
在会员表中,有着用户的注册城市信息;在销售表中记录了每个订单的目标送达城市。在网络上购买物品寄送到别的城市,很大可能是寄送给自己的父母或者朋友。对于企业来说,分析以下注册城市和送货城市的关系是一种有意义的尝试。
但是full outer join只有在企业版中能运用。
SELECT s.member_id,s.delivery_city,m.member_id,m.city
FROM sales AS s
FULL Outer join Members m ON s.member_id = m.member_id;
2.5 用SQL聚合数据
GROUP BY语句
功能:可以实现按照特定字段包含的分类进行汇总计算(比如求最小值、最大值、平均值及求和等等)
SELECT P.Base_Category,P.Category,SUM(s.amount)
FROM sales AS S INNER JOIN product AS P ON s.product_id = p.Product_ID
GROUP BY Base_Category,Category;
数据库系统会根据GROUP BY语句把我们所需要进行汇总的基准变量找到(这里也就是group by语句后面的产品大类及产品小类)
ORDE BY语句
用法:在SELECT FROM语句后面加上ORDER BY语句可以实现将显示结果按照特定字段进行排序的效果。
如果希望按照从高到低的排列效果,可以在ORDER BY语句的最后加入desc从而达到降序排列的效果
Having语句
将Having语句添加在group by语句的最后可以起到筛选出想要的结果的效果
- Having语句和Where语句两者之间的区别:
- WHERE语句中使用的筛选变量是表中数据表的原始变量(未经过任何汇总计算)
- HAVING语句中使用的筛选变量一般是做过聚合运算处理之后计算出来的新变量
- 也就是说,通常而言,having语句的前面一定要加一条group by的汇总语句。而Where语句的后面则不一定需要加group by语句
case when语句
功能:根据是否满足语句中的判断条件会落入不同的取值
CASE WHEN 如果
THEN 那么
ELSE 否则
END来提示系统整个循环语句已经结束了
2.6 SQL进阶案例
2.7 写SQL到写好SQL
- 书写代码时提供尽可能多的注释,可以使用SQL中的注释符//
- 只对我们所需要的数据进行连接join,这就要求最好在join语句中就进行where筛选,对于我们所需要的核心字段,进行后面的join操作,对于那些并不会用来做后续分析的字段,应该及时舍弃掉。
- 谨慎选择连接数据表join的方式:inner join是几种join方式中最有效率,匹配速度最快的,因为其涉及到的数据量最小
- 编写代码时要注意及时换行
库的执行顺序:
- 首先找FROM和JOIN中所提到的数据表,确定所需要的数据表是否存在
- 根据WHERE语句中筛选条件对记录进行筛选,只保留满足条件的记录
- 如果有的话,执行聚合语句GROUP BY和筛选语句HAVING的部分
- 最后执行SELECT部分的相关语句
例如,想用leftjoin语句给销售表连接上会员表中女性会员的信息,但是仍然保留销售表中男性会员的销售记录。
对比一下下面两段代码
-- 第一段代码
SELECT * FROM Sales a
LEFT JOIN members b ON a.member_id = b.member_id
WHERE b.gender = 'W';
-- 第二段代码
SELECT * FROM Sales a
LEFT JOIN(SELECT * FROM members WHERE gender = 'W') b
ON a.member_id = b.member_id;
第一段代码中男性会员的销售记录并未保存,这是因为SQL中先运行where语句,确保最后得到的记录全是女性,然后再运行join语句
正确的代码应该是将where语句放入子查询中,强行改变执行顺序。
在实际书写SQL代码时,可以先写SELECT语句,再写FROM语句,给所有需要用到的数据表起个别名,在返回到写SELECT语句后面所需要调用的详细字段。