数据分析或者是偏向数据分析的数据开发岗,要求无非就是SQL、Python和业务相关的问题。
1 SQL问答
基本这些问题和期末考试的难度比,是简单的。和学校所教的比,基本超纲的问题只会有窗口函数。这一部分面试官一般不会问你难的问题,例如三范式、事务、完整性、锁这些的。他要是问了,请反思你自己是不是说了什么装逼的话,或者简历写了什么装逼的东西,惹面试官不开心了哈哈哈,一般这部分不会为难人的。
我遇到的高频问题无非以下几点:
1.1 窗口函数
这么多次面试,窗口函数被问到的次数最多,尤其是row_number()这个。我这里推荐一篇博客,专门讲窗口函数,讲解十分细致,对我帮助很大:
常用的窗口函数https://blog.csdn.net/olizxq/article/details/118652943最喜欢问的问题无非是这两个:
- 解决排名问题,e.g.每个班级按成绩排名
- 解决TOPN问题,e.g.每个班级前两名的学生
这两个问题实际上是一个问题,就是在考察窗口函数row_number()的使用。窗口函数的SQL语句格式如下:
select 需要的属性,窗口函数名称
over (
partition by 用于分组的列名,
order by 用于排序的列名
)
其他常问的还有连接,会让你辨析内连接、外连接、交叉连接(笛卡尔积)和自然连接。这里也推荐了一篇博文用于复习:
sql常见四种连接查询https://mp.weixin.qq.com/s?__biz=MzI3NDc4NTQ0Nw==&mid=2247535446&idx=1&sn=88c720410f296446693b54d8f7e8c9c5&chksm=eb0cbe9edc7b378888c73f197cd2e088556c07ae45608bc6599aa583899d87198c6eff9b49e5&scene=27
1.2 视图View
还会问视图(view),基本回答以下内容就可以了:
视图是一种虚拟表 ,本身是不具有数据的,占用很少的内存空间,它是 SQL 中的一个重要概念。视图view存在于内存中,而表table是持久化于硬盘中的。
要注意的是:视图的创建和删除只影响视图本身,不影响对应的基表。但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。
视图较表来比,有什么优势?一是速度和效率:vie存在于内存中,数据量大时,我们把经常查询的结果集放到虚拟表中可以大大提高查迅速度。二是安全问题,视图可以将基表的一些敏感数据不显示出来,在展示给客户看时十分方便。
1.3 模式Schema
常见问题是:模式schema和数据库database的区别?
schema在概念上指的是一组DDL语句集,该语句集完整地描述了数据库的结构。还有一种是物理上的 Schema,指的是数据库中的一个名字空间,它包含一组表、视图和存储过程等命名对象。物理Schema可以通过标准SQL语句来创建、更新和修改。
具体来说,MySQL没有很好的区别Schema和Database,所以看似是个近义词。而其他数据库中Database的范围是大于Schema的,你可以认为Schema是Database的子集。
2 Python问答
一般纯数据分析对于python的要求很低,几乎只会问你有没有用过numpy生态圈的东西,例如pandas,matplotlib,numpy这些。但是数据开发需要写python脚本放到服务器上去运行,自然就会有一般Python开发对Python的要求,这里也整理了几个我遇到比较多的Python八股文:
2.1 请简述Python 内存管理与垃圾回收机制
CPython解释器内存管理主要分为三部分: 引用计数、标记清除、分代回收。
1. 引用计数
Python中的每一个对象对应着PyObject结构体,它的内部有一个名为ob_refcnt的引用计数器成员变量。程序在运行的过程中ob_refcnt的值会被更新并反映引用有多少变量引用到该对象。当对象的引用计数值为0时,它的内存就会被释放掉。
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
以下情况会导致引用计数加1:
- 对象被创建
- 对象被引用
- 对象作为参数传入到一个函数中
- 对象作为元素存储到一个容器中
以下情况会导致引用计数减1:
- 用del语句显示删除对象引用
- 对象引用被重新赋值其他对象
- 一个对象离开它所在的作用域
- 持有该对象的容器自身被销毁
- 持有该对象的容器删除该对象
2. 标记清除(Mark and Sweep)
该算法分为两个阶段:
- 标记阶段,遍历所有的对象,如果对象是可达的(被其他对象引用),那么就标记该对象为可达;
- 清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收。
CPython底层维护了两个双端链表。一个链表存放着需要被扫描的容器对象(链表A),另一个链表存放着临时不可达对象(链表B)。链表中每个节点除了有记录当前引用计数的ref_count变量外,还有一个gc_ref变量,这个gc_ref是ref_count的一个副本,所以初始值为ref_count的大小。
- 执行垃圾回收时,首先遍历链表A中的节点,并且将当前对象所引用的所有对象的gc_ref减1,主要作用是解除循环引用对引用计数的影响;
- 再次遍历链表A中节点,若节点gc_ref值为0,该对象就被标记为“暂时不可达”(GC_TENTATIVELY_UNREACHABLE)并被移动到链表B中;若节点gc_ref不为0,该对象就会被标记为“可达”(GC_REACHABLE)。对于“可达”对象,还要递归的将该节点可到达的节点标记为“可达”;链表B中被标记为“可达”的节点要重新放回到链表A中。
- 在两次遍历后,链表B中的节点就是需要释放内存的节点。
3. 分代回收
在循环引用对象的回收中,整个应用程序会被暂停。为了减少应用程序暂停的时间,Python通过分代回收(空间换时间)的方法提高垃圾回收效率。分代回收的基本思想是:对象存在的时间越长,是垃圾的可能性就越小,应该尽量不对这样的对象进行垃圾回收。CPython将对象分为三种世代分别记为0、1、2,每一个新生对象都在第0代中。
- 如果该对象在一轮垃圾回收扫描中存活下来,那么它将被移到第1代中;
- 如果在对第
1
代进行垃圾回收扫描时,该对象又存活下来,那么它将被移至第2代中; - 分代回收扫描的门限值可以通过gc模块的get_threshold函数来获得,该函数返回一个三元组,分别表示多少次内存分配操作后会执行0代垃圾回收,多少次0代垃圾回收后会执行1代垃圾回收,多少次1代垃圾回收后会执行2代垃圾回收。注意: 若执行一次2代垃圾回收,那么年轻代都要执行垃圾回收。
2.2简述Python装饰器
装饰器是一个接收函数作为参数的闭包函数。它经常用于有切面需求的场景。比如:插入日志、性能测试、事务处理、缓存、权限校验等。
作用:
- 它能使函数的功能得到扩充,而同时不用修改函数本身的代码。
- 它能够增加函数执行前、执行后的行为,而不需对调用函数的代码做任何改变。
闭包函数:一个函数的返回值是另外一个函数,返回的函数调用父函数内部的变量,如果返回的函数在外部被执行,就产生了闭包。
闭包函数的作用:使函数外部能够调用函数内部放入属性和方法。
闭包函数的优缺点:
优点:使函数外部能够调用函数内部放入的属性和方法。
缺点:闭包操作导致整个函数的内部环境被长久保存,占用大量内存。
2.3 简述Python的深拷贝、浅拷贝和等号赋值
深拷贝:新建一个对象,把原来对象的内存完全复制过来,改变复制后的对象,不会改动原来内存的内容。(两个对象在复制之后是完全独立的对象)类似于cpp的赋值,此时会在内存中新开辟空间,两个不同变量存放在内存的地址是不同的。
浅拷贝:
分两种情况:
1. 浅拷贝的值是不可变对象(数值、字符、元组)时:
和下述的等号赋值一样,对象的id值和浅拷贝原来的值相同。
2. 如果是可变对象(列表、字典等)
a. 一个简单的没有嵌套的对象,复制前后的对象相互之间不会影响。
b. 对象中有复杂子对象,如列表嵌套,如果改变原来的对象中复杂子对象的值,浅拷贝的值也会受影响,因为在浅拷贝时只复制了子对象的引用(只拷贝父对象)。类似于cpp的引用,两个不同变量存放在内存的地址是一致的。
等号赋值:相当于为原来的对象打一个新的标签,两个引用指向同一个对象,修改其中的一个,另一个也会产生变化。
2.4 简述Python多线程
Python并不支持真正意义上的多线程,Python提供了多线程包。
Python中有一个叫Global Interpreter Lock(GIL)的东西,它能确保你的代码中永远只有一个线程在执行。经过GIL的处理,会增加执行的开销。这就意味着如果你先要提高代码执行效率,使用threading不是一个明智的选择,当然如果你的代码是IO密集型,多线程可以明显提高效率,相反如果你的代码是CPU密集型的这种情况下,多线程大部分情况很差。
2.5 请辨析生成器和迭代器
生成器是一种特殊迭代器。生成一系列的值用于迭代,在for循环的过程中不断计算出下一个元素并在恰当的条件结束循环。可以使用yield的函数,返回每一次循环的迭代器。
迭代器是访问集合元素的一种方式,他的对象从集合的第一个元素开始访问,直到所有元素被访问完结束,用iter()创建迭代器,调用next()函数获取对象(迭代只能往前不能后退)。
两者区别:
生成器创建一个函数,用关键字yield生成/返回对象,而迭代器用内置函数iter()和next()。
生成器中yield语句的数目可以自己在使用时定义,迭代器不能超过对象数目。生成器yield暂停循环时,生成器会保存本地变量的状态;迭代器不会使用局部变量,更节省内存
生成器yield和函数return:
生成器yield和函数return的主要区别在于函数 return a value,生成器 yield a value同时标记或记忆 point of the yield 以便于在下次调用时从标记点恢复执行。 yield 使函数转换成生成器,而生成器反过来又返回迭代器。
2.6 python的数据类型
可变数据类型:当该数据类型对应的变量的值发生了变化时,如果它对应的内存地址不发生改变,那么这个数据类型就是 可变数据类型。
不可变数据类型:当该数据类型对应的变量的值发生了变化时,如果它对应的内存地址发生了改变,那么这个数据类型就是 不可变数据类型。
不可变数据类型:数值类型(int、float、bool)、string(字符串)、tuple(元组)
可变数据类型:list(列表)、dict(字典)
关于Python,如果还有什么新的问题,我会继续补充。
3 与业务相关的问题(未完待续)
SQL和Python的问答一般都是考验应聘者的基础知识能力,我个人认为业务相关的问题才是重中之重。面试官会让你介绍你之前的实习和项目然后再根据你的回答情况往下问一些大数据组件相关的问题。
有机会我会继续补充相关的问题的。未完待续......