python面试题网络编程和数据库

Python面试重点(进阶篇)

注意:只有必答题部分计算分值,补充题不计算分值。

第一部分 必答题

  1. 简述 OSI 7层模型及其作用?(2分)

    应用层 在应用层中封装实际的消息数据(HTTP,HTTPS,FTP)
    表示层
    会话层
    传输层 端口 udp tcp 四层交换机 四层路由器
    网络层 Ipv4 路由器 三层交换机
    数据链路层 mac arp协议 网卡 交换机
    物理层 打成数据包,变成二进制的字节流通过网络进行传输
    
  2. 简述 TCP三次握手、四次回收的流程。(3分)

    # 1.三次握手是tcp协议建立连接的过程
                    # 2.由客户端发起一个syn请求,服务端接收并回复     (synack)
                    #   客户端收到ack和syn之后再回复一个ack
                    # 3.在原生的socket代码中三次握手是由accept connect
       2.四次挥手是tcp协议断开连接的过程
       由客户端发起一个seq断开请求
       服务器接到请求后回复ack参数,表示知道了,但是这是单方面的断开服务器还可以给客户端发送数据
       服务器也给客户端发送seq请求,客户端接到请求后,会发送ack参数给服务器表示同意断开。到目前为止完成四次挥手
       
        
    
  3. TCP和UDP的区别?(3分)

    tcp:面向连接,可靠,速度慢,长度不受限,全双工,流式传输
    udp 无连接 面向数据报 不可靠 速度快 长度受限制 一对一 一对多 多对多
    
  4. 什么是黏包?(2分)

    数据传输过程由于数据大或者数据传输频率快导致数据不准确
    

情况1:

	在发送端,数据小,时间间隔短,容易几个数据粘合在一起
	# 情况2:
	在接受端,接受数据慢,在缓存区,导致几个数据粘合在了一起


# 解决:
	使用struct:
		# pack (数据长度在21个亿左右)
		"""把任意长度的数字转换成具有4个字节固定长度的字节流"""
		res = struct.pack("i",2100000000) #代表当前转化的数据是整型
		# unpack
		"""把4个字节值恢复成原来的数据,返回的是一个元组"""
		tup = struct.unpack("i",res)[0] # 把rev转换成整型int

	思路方面:
		计算接下来要发送的数据大小是多少
		通过pack转化固定4个字节发送给接受段
		然后在发送真实数据
		接受段需要接受2次,第一次接受转换成的真实数据大小,放recv参数中
		第二次在接受真实的数据,才能保证不黏包
		
	场景:
		用在及时通讯类中,如果是上传下载不需要.



5. 什么 B/S 和 C/S 架构?(2分)

B/S:浏览器和服务器
C/S:客户端和服务端




6. 请实现一个简单的socket编程(客户端和服务端可以进行收发消息)(3分)

sever端

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)

client端

import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字

一.TCP 服务端

import socket
# 1.创建一个socket对象
sk = socket.socket()
# 2.绑定ip和端口(注册网络)
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
# 3.开启监听
sk.listen()
# 4.建立三次握手
conn,addr = sk.accept()
# 5.处理收发数据逻辑
# 接受数据
msg = conn.recv(1024)
msg.decode("utf-8")
# 发送数据
conn.send(b"abc")
conn.send("我好帅哦".encode())
# 6.四次挥手
conn.close()
# 7.退还端口
sk.close()

二.TCP 客户端

# 1.创建一个socket对象
sk = socket.socket()
# 2.与服务器进行连接
sk.connect( ("127.0.0.1",9000) )
# 3.收发数据的逻辑
# 发送
sk.send(b"abc")
# 接受
sk.recv(1024)
# 4.关闭链接
sk.close()

三.TCP / socketserver 支持TCP的并发操作

import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request

if name == "main":
server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , MyServer )
server.serve_forever()

四.UDP服务端

import socket

1.创建一个socket对象

sk = socket.socket(type=socket.SOCK_DGRAM)

2.绑定地址

sk.bind( ("127.0.0.1",9000) )

3.处理收发数据的逻辑(服务器一定第一次是接受数据)

接受

msg , cli_addr = sk.recvfrom(1024)

发送

sk.sendto(b"abc" , cli_addr)

4.关闭udp连接

sk.close()

五.UDP客户端

1.创建一个socket对象

sk = socket.socket(type=socket.SOCK_DGRAM)

2.收发数据

sk.sendto("你好".encode("utf-8") , ("127.0.0.1",9000) )
sk.recvfrom(1024)

3.关闭udp连接

sk.close()




7. 简述进程、线程、协程的区别?(3分)

进程,线程,协程的区别:三个都可以提高并发

  • 进程是计算机分配资源的最小单位;线程是计算机中cpu 调度的最小单位
  • 协程又称为“微线程”,是基于代码创造出来的,而进程和线程都是计算机中真实存在的,一个进程可以有多个线程,一个线程可以创造多个协程
  • 计算密集型:多进程
  • io密集型:多线程或协程+io切换
  • 单纯的协程没有办法提高并发,只是代码之间的来回切换,叫上io才有意义

进程:资源分配的最小单位,进程之间的数据彼此隔离,可以并发并行
from multiprocessing import Process
线程:程序调度的最小单位,进程里面包含线程,共享同一份进程资源,只能并发(GIL锁)
from threading import Thread
协程:实现单线程在多任务之间的自由切换,是线程执行任务的一种方式
import gevent;from gevent import monkey
monkey.pathch_all() # 识别所有模块中的阻塞
g2 = gevent.spawn(play)




8. 什么是GIL锁?(2分)

GIL,全局解释器锁

同一时刻保证一个进程中只有一个线程可以被cpu调度,所以在使用Python开发时要注意:
计算密集型,用多进程.
IO密集型,用多线程.

并发:同一时间,一个cpu执行多个任务
并行:同一时间,多个cpu执行多个任务
GIL:全局解释器锁,为了保证数据安全,只让多线程并发,不能并行
在后台一个个的程序都是由一个个的cpython解释器执行的,每个解释器运行的程序都是单独的进程
但是同一时间,程序中的多个线程只能由一个cpu执行

解决办法:
	1.换个jpython等其他解释器,又可能出现兼容性问题
	2.用多进程的方式间接实现多线程,资源开销较大
	历史遗留问题,无法彻底解决



9. 进程之间如何进行通信?(2分)

ipc通信
IPC :
1.管道Pipe(进程和进程之间只能单向通信)(了解)
2.Queue(进程和进程之间可以双向通信)
3.文件 (共享数据)

q = Queue(3)
put get
put_nowait get_nowait (linux有兼容性问题)
empty full qsize(队列长度)



10. Python如何使用线程池、进程池?(2分)

 ```
 import time
 from concurrent.futures import ThreadPoolExecutor
 import threading
 lock=threading.RLock()
 def start(i,n):
     print(id(lock))
     time.sleep(1)
     with lock:
         print("任务1")
 fool=ThreadPoolExecutor(10)
 for i in range(100):
     fool.submit(start,i,1)
 
 print("end")
 fool.shutdown(True)
 print("所有任务执行结束")
 
 
 # import time
 # from concurrent.futures import ProcessPoolExecutor
 #
 # def tst(i):
 #     time.sleep(1)
 #     print("任务")
 #
 # if __name__ == '__main__':
 #
 #     fool=ProcessPoolExecutor(10)
 #     for i in range(100):
 #         fool.submit(tst,i)
 #     print("end")
 #     fool.shutdown()
 #     print("所有任务结束")

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
# (1)创建进程池/线程池对象 8个
 p = ProcessPoolExecutor() # 参数:cpu的逻辑处理器数量
 p = ThreadPoolExecutor()  # 参数:cpu的逻辑处理器数量 * 5
# (2)提交异步任务submit
 res = p.submit(func,参数1,参数2,...)
# (3)获取返回值 result (里面有阻塞)
 res_new = res.result()
# (4)等待所有子进程执行完毕 shutdown
 p.shutdown()
 print("主进程执行结束 .. ")
 
 ```

 

11. 请通过yield关键字实现一个协程? (2分)

 ```
# 创建生成器
(1) 生成器表达式: gen = (i for i in range(10))
(2) 生成器函数  : 函数内含有yield,需要初始化才能使用
"""
def producer():
 for i in range(100):
 	n = yield i
 	print("结果:%s",n)

def consumer():
 # 生成器函数的初始化
 g = producer()
 # send可以类比next,但是第一次调用时,必须给None,send可以给yield发送数据(上一个yield)
 g.send(None)
 for i in range(10):
 	res = g.send(i)
 	print(res)
consumer()
 ```

 

12. 什么是异步非阻塞? (2分)

 ```
 # 异步 : 一个程序在执行中调用了另一个 但是不等待这个任务完毕 就继续执行 start
同步:
 代码从上到下按照顺序,依次执行
异步:
 无需等待当前程序中的代码是否执行完毕,
 该代码又开启另外一个进程/线程中执行

阻塞   : input , time , sleep , recv ...
非阻塞 : 依次执行,无需等待

异步非阻塞:
 场景发生在多进程/多线程之间
 没有任何io阻塞或者等待,同时执行
 设置setblocking(False) (设置非阻塞 了解)
 ```

 

13. 什么是死锁?如何避免?(2分)

 ```
 from threading import Lock
 互斥锁,死锁,递归锁
 只上锁不解锁是死锁
 例如:
 	lock = Lock()
 	# 1
 	lock.acquire()
 	lock.acquire()
 	lock.acquire()
 	
 	# 2
 	进程1
 	a.acquire()
 	b.acquire()
 	b.release()
 	a.release()
 	
 	进程2
 	b.acquire()
 	a.acquire()
 	a.release()
 	b.release()
 	进程1 拿着A锁抢B锁
 	进程2 拿着B锁抢A锁
 
 # 使用递归锁,快速应急,解决服务器死锁问题
 	a = b = RLock()
 	
 	a.acquire()
 	a.acquire()
 	a.acquire()
 	
 	a.release()
 	a.release()
 	a.release()
 多次上锁的次数和多次解锁的次数相同,就能达到解锁的目的;
 以后使用锁时,尽力不用锁嵌套;


 ```

 

14. 程序从flag a执行到falg b的时间大致是多少秒?(2分)

 ```守护进程: 守护的是主进程
守护线程: 守护的是所有线程;

# 0~1秒
import threading
import time
def _wait():
 time.sleep(60)
# flag a
t = threading.Thread(target=_wait)
t.setDeamon(False)
t.start()
# flag b 



 ```

15. 程序从flag a执行到falg b的时间大致是多少秒?(2分)

 ```python
 import threading
 import time
 def _wait():
 	time.sleep(60)
 # flag a
 t = threading.Thread(target=_wait)
 t.setDeamon(True)
 t.start()
 # flag b
 0.01秒
 ```

16. 程序从flag a执行到falg b的时间大致是多少秒?(2分)

 ```python
 import threading
 import time
 def _wait():
 	time.sleep(60)
 # flag a
 t = threading.Thread(target=_wait)
 t.start()
 t.join()
 # flag b
 60秒
 ```

17. 读程序,请确认执行到最后number是否一定为0(2分)

 ```python
 import threading
 loop = int(1E7)
 def _add(loop:int = 1):
 	global number
 	for _ in range(loop):
 		number += 1
 def _sub(loop:int = 1):
 	global number
 	for _ in range(loop):
 		number -= 1
 number = 0
 ta = threading.Thread(target=_add,args=(loop,))
 ts = threading.Thread(target=_sub,args=(loop,))
 ta.start()
 ta.join()
 ts.start()
 ts.join()
 是
 ```

18. 读程序,请确认执行到最后number是否一定为0(2分)

 ```python
 import threading
 loop = int(1E7)
 def _add(loop:int = 1):
 	global number
 	for _ in range(loop):
 		number += 1
 def _sub(loop:int = 1):
 	global number
 	for _ in range(loop):
 		number -= 1
 number = 0
 ta = threading.Thread(target=_add,args=(loop,))
 ts = threading.Thread(target=_sub,args=(loop,))
 ta.start()
 ts.start()
 ta.join()
 ts.join()
 否
 ```

19. MySQL常见数据库引擎及区别?(3分)

 ```
       
 # 存储引擎的种类
     # innodb : 索引+数据 表结构  数据的持久化存储
         # 事务 :一致性 n条语句的执行状态是一致的
             # begin;   # 开启事务
             # select id from innot where id =1 for update;
             # update innot set id = 2 where id = 1;
             # commit;  # 提交事务 解锁被锁住的数据,让他们能够被修改
         # 行级锁 :只对涉及到修改的行加锁,利于并发的修改,但是对于一次性大量修改效率低下
         # 表级锁 :一次性加一把锁就锁住了整张表,不利于并发的修改,但是加锁速度比行锁的效率要高
         # 外键约束 :被约束表中的数据不能随意的修改/删除 约束字段据要根据被约束表来使用数据
     # myisam : 索引 数据 表结构  数据的持久化存储
         # 表级锁
     # memory : 表结构
         # 数据断电消失
 ```

 

20. 简述事务及其特性? (3分)

 ```
 # 事务 :一致性 n条语句的执行状态是一致的
             # begin;   # 开启事务
             # select id from innot where id =1 for update;
             # update innot set id = 2 where id = 1;
             # commit;  # 提交事务 解锁被锁住的数据,让他们能够被修改
A.原子性:
 同一个事务当中可能执行多条sql语句,要么全部成功,要么直接回滚,这个过程看成一个整体,一个不能再分割的最小个体
C.一致性:
 a,i,d 都是为了保证数据的一致性提出来的
 比如必须按照约束要求插入数据,保证每跳数据类型的一致性
 事务角度上,防止脏读,幻读,不可重读,最终决定当前客户端和当前的数据库状态一致
I.隔离性:
 lock + isolation锁,来处理事务的隔离级别;
 一个事务和另外一个事务在工作过程中彼此隔离独立
 如果同时更改同一个数据,因为锁机制的存在,先执行的先改,其他事务需要等待,保证数据安全
D.持久性:
 把数据写在磁盘上,保证数据的持久化存储;

 ```

 

21. 事务的隔离级别?(2分)

 ```
 第一种隔离级别:Read uncommitted(读未提交)
 如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据,该隔离级别可以通过“排他写锁”,但是不排斥读线程实现。这样就避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据
 
 解决了更新丢失,但还是可能会出现脏读
 
 第二种隔离级别:Read committed(读提交)
 如果是一个读事务(线程),则允许其他事务读写,如果是写事务将会禁止其他事务访问该行数据,该隔离级别避免了脏读,但是可能出现不可重复读。事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
 
 解决了更新丢失和脏读问题
 
 第三种隔离级别:Repeatable read(可重复读取)
 可重复读取是指在一个事务内,多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务(包括了读写),这样避免了不可重复读和脏读,但是有时可能会出现幻读。(读取数据的事务)可以通过“共享读镜”和“排他写锁”实现。
 
 解决了更新丢失、脏读、不可重复读、但是还会出现幻读
 
 第四种隔离级别:Serializable(可序化)
 提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读
 
 解决了更新丢失、脏读、不可重复读、幻读(虚读)
 

脏读: 没提交的数据读出来的 (查)
不可重读: 前后多次读取,数据内容不一样(同一个会话中,在不进行修改或者删除的时候,永远看到的是同一套数据)
幻读:前后多次读取,数据内容不一样(从添加的角度上说的)

# 开始事务
begin:
# 处理sql
# commit 提交数据
# rollback 回滚数据

# 数据的隔离级别

RU(READ_UNCOMMITTED) : 读未提交 : 脏读,不可重读,幻读
RC(READ_COMMITTED)   : 读已提交 : 防止脏读,会出现不可重复还有幻读
RR(REPEATABLE_READ)  : 可重复读 : 防止脏读,不可重复读,可能会出现幻读(默认隔离级别)
SR(SERLALIZABLE)     : 可序列化 : 什么都能防止(多个窗口同步,不能并发,性能差)


# 查看默认的隔离级别
select @@tx_isolation
# 查询是否自动提交数据
select @@autocommit
# 找到my.ini 配置文件
autocommit=0 # 关闭自动提交数据
transaction_isolation = READ_UNCOMMITTED # 设置隔离级别

# 打开窗口1
begin;
update t1 set name = "abc" where id = 1
# commit;

# 打开窗口2
select * from t1;

 ```

 

22. char和varchar的区别?(2分)

 ```
 char和varchar都是定义字符串 但是varchar定义的是自动调节开辟的空间,用不了可以回收
 ```

 

23. mysql中varchar与char的区别以及varchar(50)中的50代表的含义。(2分)

 ```
 50表示最大可以存储的长度
 ```

 

24. MySQL中delete和truncate的区别?(2分)

 ```
 delete表示删除数据,但是还是用删除前的id
 truncate也表示删除,但是删除以后重新进行id排序
 ```

 

25. where子句中有a,b,c三个查询条件, 创建一个组合索引abc(a,b,c),以下哪种会命中索引(3分)

 ```
 (a)1
 (b)
 (c)
 (a,b)1
 (b,c)
 (a,c)1
 (a,b,c)1
 ```

26. 组合索引遵循什么原则才能命中索引?(2分)

 ```
 开头从左边的索引开头
 ```

 

27. 列举MySQL常见的函数? (3分)

 ```
 count 
avg
sum 
max
min
now() 
concat
concat_ws
user => select user()
databases => select databases()
group_concat
year(),month,day(),hour,minute,second week...
password
 ```

 

28. MySQL数据库 导入、导出命令有哪些? (2分)

 ```

导出 (q退出数据库)
mysqldump -uroot -p123 db1 > db1.sql
mysqldump -uroot -p123 db1 表1 表2 表3 > ceshi100.sql
# 导入 (进入到mysql,选好数据库)
source /home/wangwen/work/abc.sql
 1.导入
 语法格式:mysql -h ip -u userName -p dbName < sqlFilePath (最后没有分号) 
 -h : 数据库所在的主机。如果是本机,可以使用localhost,或者省略此项; 
 -u : 连接数据库用户名。 
 -p : 连接数据库密码。出于安全考虑,一般不在-p之后直接写出明文的密码。整个命令回车之后,数据库会要求输入密码,那个时候再输入密码将以**的形式显示出来。有一定的保护作用。 
 dbName : 要使用的具体的某个数据库。这个不是必须的,如果sql脚本中没有使用“use dbName”选择数据库,则此处必须制定数据库;如果使用了”use dbName”,则可以省略。 
 sqlFilePath : sql脚本的路径。如我将sql脚本放在了D盘,我的sql脚本的名字是”test_sql.sql”。则路径为”D:	est_sql.sql”。
 
 2.命令行导出
 导出某个数据库:
 
 mysqldump -u root -p dbName > sqlFilePath
 
 导出多个数据库:
 
 mysqldump -u root -p –add-drop-database –databases dbName1 dbName2… > sqlFilePath 
 –add-drop-database : 该选项表示在创建数据库的时候先执行删除数据库操作 
 –database : 该选项后面跟着要导出的多个数据库,以空格分隔
 
 导出某个数据库的某个表:
 
 mysqldump -u root -p dbName tableName > sqlFilePath
 
 只导出数据库结构,不带数据:
 
 mysqldump -u root -p -d dbName > sqlFilePath 
 -d : 只备份结构,不备份数据。也可以使用”–no-data”代替”-d”,效果一样。
 ```

 

29. 什么是SQL注入?(2分)

 ```
 sql注入:通过注入一些特殊的字符,绕开sql的判断机制
# 使用预处理机制,可以尽量避免sql注入
execute 默认参数是一条sql语句,如果加入参数元组,就等于开启预处理
语法:execute(sql,(参数1,参数2,参数3......))


import pymysql
user = input("user>>>:").strip()
pwd = input("password>>>:").strip()

conn = pymysql.connect(host="127.0.0.1",user="root",password="",database="db2")
# 创建游标对象
cursor = conn.cursor()
# 方法一
"""
user>> sdfsd
password>> sdfsdf' or 10=10 -- sdfsdfsf
sql = "select * from usr_pwd where username = '%s' and password='%s'  " % (user,pwd)
res = cursor.execute(sql)
print(res) #返回条数
"""

# 方法二
sql = "select * from usr_pwd where username = %s and password=%s"
res = cursor.execute(sql,(user,pwd))

if res:
 print("登录成功")
else:
 print("登录失败")
 ```

 

30. 简述left join和inner join的区别?(2分)

 ```
left join  : 左联 以左表为主,右表为辅,完整查询左表所有数据,右表不存在的数据拿null来补 
inner join : 内联 查询左表右表共同存在的数据 select * from a,b where a.cid = b.id
 ```

 

31. SQL语句中having的作用?(2分)

 ```
 一般和group by 配合使用,将分组之后的数据进行二次过滤用having
 ```

 

32. MySQL数据库中varchar和text最多能存储多少个字符?(2分)

 ```
 一个TEXT列,最大长度为65535(2^16-1)个字符
 varchar(n) 表示n个字符,无论汉字和英文,MySql都能存入 n 个字符,仅实际字节长度有所区别。
 ```

 

33. MySQL的索引方式有几种?(3分)

主键primary key 唯一索引 unique 普通索引 index
联合主键primary key(字段1,字段2,...)
联合唯一索引 unique(字段1,字段2,..)
联合普通索引 index(a,b,c)

innodb(聚集索引) : 一个表只有一个聚集索引,和多个辅助索引,排序速度比较快
myisam(辅助索引) : 只能有多个辅助索引,没有聚集索引

myisam 和innodb 使用索引数据结构都是b+树,只是叶子节点存储的数据不同
innodb文件结构中只有.frm 和 .ibd, 直接把数据塞到叶子节点上
myisam文件结构中只有.frm .myd .myi 叶子节点存储的该数据的地址(映射关系)

34. 什么时候索引会失效?(有索引但无法命中索引)(3分)

1.如果查询的是一个大范围内的数据(like in > < ....) 不能命中索引(
2.索引字段参与运算,不能命中,select * from s1 where id*3 = 600
3.如果有or相连,索引字段的判断条件在or的后面,不能命中索引
4.类型不匹配,不能命中 select * from s1 where first_name = 1000
5.联合索引中,不符合最左前缀原则的,不能命中索引
6.like以%开头

35. 数据库优化方案?(3分)

1.读写分离(主从数据库,主数据库查询,从数据库负责增删改)
2.分库分表(将字段数量过多的表进行拆分)
3.合理优化数据类型,尽量少的占用空间以合理改善聚集索引b+树的高度(追求矮胖结构)

  1. 什么是MySQL慢日志?(2分)
    设定一个时间阀值,执行sql的时间超过该阈值,把该sql记录在日志文件里,就是慢查询日志

    查看日志开启状态

    show variables like 'slow_query_log';

    开启慢查询日志

    set global slow_query_log = "ON";

    查看时间阈值

    show variables like "long_query_time"

    设置时间的阈值

    set global long_query_time = 5
    ....

    参考: https://www.cnblogs.com/Yang-Sen/p/11384440.html

37. 设计表,关系如下: 教师, 班级, 学生, 科室。(4分)
    科室与教师为一对多关系, 教师与班级为多对多关系, 班级与学生为一对多关系, 科室中需体现层级关系。

    ```
    1.  写出各张表的逻辑字段
    2.  根据上述关系表
        a.查询教师id=1的学生数
        b.查询科室id=3的下级部门数
        c.查询所带学生最多的教师的id



设定一个时间阀值,执行sql的时间超过该阈值,把该sql记录在日志文件里,就是慢查询日志
	# 查看日志开启状态
	show variables like 'slow_query_log';
	# 开启慢查询日志
	set global slow_query_log = "ON";
	# 查看时间阈值
	show variables like "long_query_time"
	# 设置时间的阈值
	set global long_query_time = 5
	....
	# 参考: https://www.cnblogs.com/Yang-Sen/p/11384440.html


    ```


38. 有staff表,字段为主键Sid,姓名Sname,性别Sex(值为"男"或"女"),课程表Course,字段为主键Cid,课程名称Cname,关系表SC_Relation,字段为Student表主键Sid和Course表主键Cid,组成联合主键,请用SQL查询语句写出查询所有选"计算机"课程的男士的姓名。(3分)

staff
sid sname sex
1 张三 男
2 李四 女

course
cid cname
1 计算机
2 美术

sc_relation
sid cid
1 1

as 起别名

select
s.sname
from
staff as s,
course as c,
sc_relation as sc
where
sc.sid = s.sid
and c.cid = sc.cid
and c.cname = "计算机"
and s.sex = "男"

as 可以省略

select
s.sname
from
staff s,
course c,
sc_relation sc
where
sc.sid = s.sid
and c.cid = sc.cid
and c.cname = "计算机"
and s.sex = "男"


39. 根据表关系写SQL语句(10分)

    <img src="day02.assets/image-20191104220302950.png" alt="image-20191104220302950" style="zoom:50%;" />

    - 查询所有同学的学号、姓名、选课数、总成绩;
    - 查询姓“李”的老师的个数;
    - 查询平均成绩大于60分的同学的学号和平均成绩; 
    - 查询有课程成绩小于60分的同学的学号、姓名
    - 删除学习“叶平”老师课的score表记录;
    - 查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;
    - 查询每门课程被选修的学生数;
    - 查询出只选修了一门课程的全部学生的学号和姓名;
    - 查询选修“杨艳”老师所授课程的学生中,成绩最高的学生姓名及其成绩;
    - 查询两门以上不及格课程的同学的学号及其平均成绩;



### 第二部分 补充题

1. 什么是IO多路复用?

内部的实现是异步非阻塞,通过单个线程管理多个socket连接,而不是创建大量的多进程/多线程,节省资源,提升效率
这些网络io操作都会被selector(内部使用linux的epoll多路复用接口实现的)暂时挂起,推入内存队列
此时服务端可以任意处理调度里面的网络io,
当连接的socket有数据的时候,自然会把对应的socket告诉你然后进行读写,而不至于一直阻塞等待


2. async/await关键字的作用? 

asyncio 是在io密集型任务中,处理协程异步并发的工具模块,目的是加快通信的速度,减少阻塞等待
async def 关键字定义异步的协程函数
await 关键字加载需要等待的操作前,控制一个可能发生io阻塞任务的切入和切出


3. MySQL的执行计划的作用?

执行计划 在一条sql执行之前,制定执行的方案
"""desc/emplain + sql"""
desc select * from t1;
把执行计划的类型,优化级别从低->高
all > index > range > ref > eq_ref > const > system
目标: 至少达到range , ref;
range 索引范围扫描(注意点:如果范围太大,不能命中索引)
ref 普通索引查询(非唯一)

第四题


4. 简述MySQL触发器、函数、视图、存储过程? 

https://www.cnblogs.com/Eva-J/articles/10435035.html

5. 数据库中有表:t_tade_date

id tade_date
1 2018-1-2
2 2018-1-26
3 2018-2-8
4 2018-5-6
...
输出每个月最后一天的ID
select
id,max(tade_date)
from
t_tade_date
group by
month(tade_date)

原文地址:https://www.cnblogs.com/python25/p/12371338.html