4.3.1 SQL极简入门
SQL的全称是结构化查询语言(SQL,Structured Query Language),一种特殊目的的编程语言,一种数据库查询和程序设计语言,也是笔者认为难度最低的一种程序语言。
本节主要介绍SQL语句的基本常识,比较适合SQL语句零基础的读者。近年来,MySQL、Hive和ClickHouse数据库比较常见,笔者所在的公司现在主要使用的数据库是ClickHouse(以下简称CH)。对于并发较小的业务场景,CH的查询速度一般较快,所以用CH为例来讲解。
首先我们回顾一下这个问题:“当有一个数据需求的时候,我们是怎么做的?能否将整个过程抽象成固定的步骤?”
答案是可以。下面是具体步骤。
●步骤1,确认问题。和4.1节的数据分析方法论一致,需要清晰地描述问题是什么,主要是确认问题的限定范围,比如哪段时间。
●步骤2,寻找能描述问题的数据。此时主要是构建数据理想态的过程,不需要考虑数据是否真实存在,只需要找到能够描述问题的数据在哪儿。
●步骤3,设计数据表格。一般在Excel、Numbers等表格工具内完成,类似表4-4的格式,通过数据提取、加工得到最终输出数据。
表4-4 提出数据需求的示例表

●步骤4,找数据。在得到最终输出表结构以后,需要调研哪些数据是存在的,这些数据一般存在哪个数据表中,这些数据存储的表是否是准确的,是天级别更新还是小时级更新的。通过这个过程可以填充数据表中一部分实例,比如表4-4中的内容ID、发布时间和状态。
●步骤5,对于不清楚的数据信息需要询问数据分析师。一般公司内部每条业务线都会配备对接的数据分析师,关于本项目的数据表和数据埋点设计一般由对接的数据分析团队完成,所以一般也将数据分析师作为团队的数据接口人。策略产品经理在遇到自己无法解决的数据来源问题时一般需要求助数据接口人。
●步骤6,对目标表进行随机取样,洞悉数据格式和异常值。常用的语句为“select*from表名limit10”,使用该语句可以看到目标数据表的前10行数据,可以作为数据表预览使用。
●步骤7,写SQL语句,进入正式的数据检索。所有策略产品经理都需要不断地学习SQL语句并学会看报错提示。(许多策略产品经理只要看到SQL中的报错就十分头疼,但在错误中不断地学习SQL是一条必经之路。)
接下来,笔者介绍简单的SQL操作,零基础入门首先需要了解的内容如下。
(1)查看表的数据结构:desc。
在CH中的desc语句为:
desc表名desc语句的返回结果是CH中的数据表的字段名称和字段类型。
(2)增加搜索限定条件:where。
通常在数据搜索时需要增加限定条件,限定条件的语句是where,可以增加多个限定。在CH中的限定语句为:
select uid,feed_id from 某张表其中,uid为用户的id,feed_id为内容推荐列表中每一条内容对应的唯一id。
因为uid的数据类型为“整数型”,所以可以使用“uid=4”来进行查询,如果搜索条件为“字符串”,应该增加单引号,查询语句如下:
select uid,feed_id from 某张表feed_id是以字符串进行存储的(以字符串存储可以避免数字过长时在数据结构上的麻烦),程序设计语言中单引号的作用就是把字符串标识出来,所以数字1在SQL中写作1,而文本1则写作‘1’。
(3)增加多个限定条件:and和or。
限制多个条件时,可以在where的条件后增加其他条件,在CH中的and语句为:
select uid,feed_id from 某张表其中,uid为用户的id,feed_id为内容推荐列表中每一条内容对应的唯一id,status为该feed_id的审核状态,数据类型为文本型。
该语句的含义为:在该数据表中查找uid和feed_id两个字段,限定条件为uid是整数型4,并且feed_id是空字符串,并且审核状态为“审核通过”或“待审核”。
其中,查询语句中的“('审核通过','待审核')”是一个数组,与in语句联合使用,表示某变量是否在该数组中。
And表示“与”的关系,“A and B”表示同时满足A、B两个条件。如果想使用“或”的关系可以使用or语句,只需要把查询语句中的and全部替换为or即可,“A or B”代表A与B任意满足其中一个条件即可。
(4)排序规则:order by。
限制多个条件时,可以在where的条件后增加其他条件,在CH中的order by语句为:
select uid,feed_id from 某张表其中,uid为用户的id,feed_id为内容推荐列表中每一条内容对应的唯一id。
该语句的含义为:按照uid进行升序排列,如果希望按照uid降序排列,需要使用order by uid desc(desc是descend简写,降序)。
order by不仅可以按照结果中的某个字段进行排序,还可以按照函数结果排序,在SQL中进行随机抽样的一种常用方法如下:
select * from 某张表该语句的含义为:利用rand()函数进行升序排列,并取前1000条数据。为什么这种语句可以实现对数据的随机抽样呢?因为rand()代表输入为空,输出为0到1之间随机数的函数。比如某数据表中有10万行数据,该函数相当于为该数据表新增辅助列,且辅助列中填充的数值是随机数,然后对该辅助列进行排序,选取前1000条。
(5)基本函数:toDate等。
在SQL语言中有许多函数可以使用,且大多数程序设计语言中函数的表达方式都是一样的:
函数输出值=函数名(函数输入变量1,函数输入变量2…)
比如上述的随机数函数rand()就是一个输入为空,输出为随机数的函数。相似的还有日期函数today()(输入为空,输出为“今天日期”),这些都很常用。还有一些函数具有输入变量,比如类型转换函数toString(参数),可以将其他值转换为文本格式。无论是Hive、MySQL还是CH都有许多专有的函数,函数的学习是与数据需求同步的,做过的项目越多掌握的函数也就越多。本节旨在消除读者对于写SQL语句的恐惧心理,毕竟罗马不是一天建成的,查询语言也需要不断地累积项目经验才能掌握。
(6)聚合函数:sum、count和uniqExact。
以上介绍的SQL语句只是对数据表进行简单的条目检索,但实际工作中最常用到的是对原始数据进行聚合的相关函数。4.2节介绍的多个描述型统计量在SQL语句中可以直接计算。
表4-5 使用group by子句前的原始数据表

聚合函数需要和group by语句一起使用,可以实现“以某一离散变量进行分组,对其他某个变量计算描述型统计量”的功能。假如原始数据表如表4-5所示,共有5行数据,我们希望知道每个uid分别发布了多少篇feed(也就是具有多少个feed_id)。
在CH中的group by语句为:
select uid,uniq(feed_id) as fid_count from 表这个语句中有两个新的知识点。第一个知识点是函数uniq(参数),含义是对feed_id进行去重计数(比如有2个相同的feed_id时记为1个)。第二个知识点是group by语句,相当于“按照uid进行分组,每组uid按照feed_id进行去重计数”。结果如表4-6所示。
表4-6 使用group by子句后的数据表

group by后边的字段必须和select后边忽视掉聚合函数的字段保持一致,比如:
●select{字段1},{聚合函数}from表group by{字段1}
●select{字段1,字段2},{聚合函数}from表group by{字段1,字段2}
●select{字段1,字段2},{聚合函数1},{聚合函数2}from表group by{字段1,字段2}
●select{字段1,字段2,字段3},{聚合函数1},{聚合函数2}from表group by{字段1,字段2,字段3}
只有这样,SQL语句才不会报错,实际工作中一般使用两个及以上的变量作为group by的变量。
(7)不同表的拼接:join。
大多数情况下数据表并未整合在同一个数据表中,需要对数据做联表查询,关于在CH中的join语句为:
select * from这个语句中有三个新的知识点。第一个知识点是any和all,any代表的是作为连接的字段是任一提取,all代表的是连接的字段全部提取;第二个知识点是SQL语句中的连接方式。SQL语句中的4种join如下。
●Inner join:左表和右表的交集。
●Left join:以左表为主键的外连接。
●Right join:以右表为主键的外连接。
●Outer join:笛卡尔积。
第三个知识点是关于连接字段,在CH中一般使用using(字段)函数,在MySQL中一般使用on语句。join的语法比较复杂,本书由于篇幅所限不再赘述。
SQL的学习分为几个阶段:能看懂别人的SQL、能改别人的SQL、能写单体SQL、能写联表SQL。对于运营经理而言,一般需要做到第二步“能改”;对于功能型产品经理而言,一般需要“能写单体SQL和联表SQL”;对于策略产品经理而言,要求是不仅“能写SQL”,还需要会使用数据分析工具进行数据挖掘。本节内容旨在通过最基础的SQL教程介绍,帮助SQL语言零基础的读者建立信心,直至能找到写SQL的成就感。