Python面试重点(进阶篇)

Python面试重点(进阶篇)

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

第一部分 必答题

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

    应用层(应用层,表示层,会话层)
    在应用层中封装实际的消息数据(HTTP,HTTPS,FTP)
    传输层:
    封装端口 指定传输的协议(TCP/UDP)
    网络层:
    封装ip   版本ipv4 / ipv6
    数据链路层:
    封装mac地址 指定链路层协议: arp(通过ip->mac)/rarp(通过mac->ip)
    物理层:
    打成数据包,变成二进制的字节流通过网络进行传输

     

  2. 简述 TCP三次握手、四次回收的流程。(3分)

    三次握手
    1.客户端发送一个标识SYN的数据段,表示想与服务器建立连接,此数据段的序列号(seq)为a.
    2.服务器同意了客户端的请求回复标识了SYN+ACK的数据段,此数据段的序列号(seq)为b,确认序列号为客户端的序列号加1(a+1).以此作为对客户端的SYN报文的确认
    3.客户端再次发送一个标识ACK的数据段,此数据段的序列号为(seq)为a+1,确认序列号为服务器的序列号加1(b+1),以此作为对服务器A的SYN报文段的确认,建立连接


    四次挥手:
    1.客户端想终止连接,于是发送一个标识了FIN,ACK的数据段,序列号为a,确认序列号为b。
    2.服务器回应一个标识了ACK的数据段,序列号为b,确认序号为a+1,作为对客户端的FIN报文的确认。
    3.服务器想终止连接,于是向客户端发送一个标识了FIN,ACK的数据段,序列号为b,确认序列号为a+1。
    4.客户端回应一个标识了ACK的数据段,序列号为a+1,确认序号为b+1,作为对服务器的FIN报文的确认。



    SYN 创建连接
    ACK 确认响应
    FIN 断开连接

    # 三次握手
    客户端发送一个消息,请求建立连接
    服务端接受客户端的响应,并且发出与客户端建立连接的请求
    客户端接受服务端响应,回应服务端请求
    接下来就可以发送数据 ...

    # 四次挥手
    客户端发送一个消息,请求断开连接
    服务端接受客户端响应,回应请求
    等到所有数据收发完毕之后
    服务端发送断开连接的请求
    客户端接受服务端响应,回应请求
    等到2msl 最大报文生存时间过后
    客户端和服务端彻底断凯连接

     

  3. TCP和UDP的区别?(3分)

    cp 需要建立连接,传输可靠,速度慢,消息面向字节流无边界
    udp 不需要建立连接,可靠性差,速度快,消息面向数据报(报文)有边界

     

  4. 什么是黏包?(2分)

        # 黏包:
    tcp协议数据因为无边界的特点,导致都填分开发送的数据粘合在一起变成了一条数据

    # 现象:
    # 情况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分)

    网络开发的两大架构:
    c/s : client server 软件
    b/s : Brower server 网站,小程序

     

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

    client:
    import socket

    sk = socket.socket()
    add = input("输入IP:")
    duan = input("输入端口:")
    sk.connect((add, int(duan)))
    print(f"连接成功,开始与{add}开始通信")

    while True:
      b = input("请输入内容:")
      if b == "q":
          sk.send(b.encode('utf-8'))
          sk.close()
          break
      else:
          sk.send(b.encode('utf-8'))
      msg = sk.recv(1024).decode('utf-8')
      print(msg)
      if msg == "q":
          print("对方已断开链接")
          sk.close()
          break

    sk.close()


    server:

    import socket

    sk = socket.socket()
    sk.bind(('127.0.0.1',8598))
    sk.listen()
    conn, addr = sk.accept()
    print(f"连接成功,开始与{addr}开始通信")
    while True:
      msg = conn.recv(1024).decode('utf-8')
      print(f"{addr}:{msg}")
      if msg =="q":
          print("对方已断开链接,可以进行下次对话")
          conn.close()
          conn, addr = sk.accept()

      a = input("请输入内容:")
      conn.send(a.encode('utf-8'))
      if a =="q":
          conn.send(a.encode('utf-8'))
          conn.close()
          break

    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()

    """
    最大的网络传输数据包大小 (MTU 1500Byte)
    一般路由器网络转发数据的数据包大小不超过1500B
    超过这个范围,该数据会进行拆包和打包的过程
    """

     

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

    进程,计算机分配资源得最小单位;
    线程,计算机中cpu调度得最小单位;
    协程,又可以称为"微线程",时基于代码人为创造出来的,而进程和线程是计算机中真实存在的
    一个进程中可以有多个线程/一个线程中可以创建多个协程
    计算密集型,多线程
    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分)

    全局解释器锁 同一时刻保证一个进程中只有一个线程可以被cpu调度.

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

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

     

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

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

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

     

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

    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分)

    同步:
    代码从上到下按照顺序,依次执行
    异步:
    无需等待当前程序中的代码是否执行完毕,
    该代码又开启另外一个进程/线程中执行

    阻塞   : 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分)

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


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

    # 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分)

    import threading
    import time
    def _wait():
    time.sleep(60)
    # flag a
    t = threading.Thread(target=_wait)
    t.setDeamon(True)
    t.start()
    # flag b
    小于1秒 因为设置守护线程,子线程等待所有非子线程结束子线程结束。
  16. 程序从flag a执行到falg b的时间大致是多少秒?(2分)

    import threading
    import time
    def _wait():
    time.sleep(60)
    # flag a
    t = threading.Thread(target=_wait)
    t.start()
    t.join()
    大于60秒
    因为设置了阻塞需要等待子线程结束主线程才能结束
  17. 读程序,请确认执行到最后number是否一定为0(2分)

    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()
    一定为0,因为增删操作只有一条语句不回产生数据安全问题


    """------------------------------------------------------------
    def add(x:int,y:int) -> int:
    return x+y
    res = add(10,20)
    print(res)
    """
    # 对 是0
    import threading
    loop = int(1E7) # 1 * 10的7次幂 10000000
    def _add(loop:int = 1):
    global number
    for _ in range(loop): # for _ in range(10000000)
    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分)

    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()
    不一定,因为存在线程安全问题,同一时间修改变量值


    # 不一定
    import threading
    loop = int(1E7) # 1 * 10的7次幂 10000000
    def _add(loop:int = 1):
    global number
    for _ in range(loop): # for _ in range(10000000)
    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分)

    myisam : 5.5之前的默认存储引擎 , 只支持表级锁(读写互相阻塞)
    innodb : 5.5版本之后,默认的存储引擎,支持事务,行级锁,外键,能够抗住更大的并发量(全表扫描,存在表级锁)
    memory : 把数据存储在内存里,一般做缓存
    blackhole : 黑洞,用来同步数据,应该在主从数据库当中

     

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

    事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做 
    特性:原子性,一致性,隔离性,持久性

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

     

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

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

    # 开始事务
    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 变长,速度慢,节省空间(内容的开头会有1~2个字节存储数据长度)

     

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

    varchar(50) 最多存50个字符
    (字符长度如果小于255个,前头用1个字节存长度
    字符长度如果大于255个,前头用2个字节存长度
    1111 1111 => 255
    1111 1111 1111 1111 => 65535
    255 ~ 65535 字符长度
    )

     

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

    delete 删除数据
    truncate 重置表(删除数据+重置自增id)

     

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

    (a)
    (b)
    (c)
    (a,b)
    (b,c)
    (a,c)
    (a,b,c)


    (a)(a,b)(a,c)(a,b,c)会命中索引

    -------------------------------------------------------------------
    (a)     命中
    (b)     不行
    (c)     不行
    (a,b)   命中
    (b,c)   不行
    (a,c)   命中
    (a,b,c) 命中
  26. 组合索引遵循什么原则才能命中索引?(2分)

    最左前缀原则,条件不能使用范围,可以使用and
    # where a>1 and b=1 and c = 100 不能命中
    # where b=1 and c = 100 or a = 10 不能命中

     

  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

     

  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分)

    varchar 存的是字符 21845 最大字节数 65535 
    text   存的是字符 65535 最大字节数 65535 * 3

     

  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+树的高度(追求矮胖结构)

     

  36. 什么是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

    teacher 老师
    id name   post_id
    1 王老师   1
    2 张老师   1
    3 金角大王 2

    class 班级
    id name
    1 python1班
    2 python2班
    3 python3班

    t_c_relation 多对多关系
    id tid cid
    1 1   1
    2 1   2
    3 3   1
    4 3   2

    student 学生
    id name class_id
    1 李四 1
    2 张三 2

    post 部门
    id name   parent_id
    1 教务部   0
    2 python部 1
    3 linux部 1

    # a
    select
    count(*)
    from
    t_c_relation as tc,student as s
    where
    tc.cid = s.class_id
    and
    tc.tid = 1

    # b
    select
    count(*)
    from
    post
    where
    parent_id = 3

    # c
    select
    tc.tid,count(*) as c
    from
    t_c_relation as tc,student as s
    where
    tc.cid = s.class_id
    and
    tc.tid = 1
    group by
    tc.tid
    order by
    c desc
    limit 1
  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分)

     

    • 查询所有同学的学号、姓名、选课数、总成绩;

    • 查询姓“李”的老师的个数;

    • 查询平均成绩大于60分的同学的学号和平均成绩;

    • 查询有课程成绩小于60分的同学的学号、姓名

    • 删除学习“叶平”老师课的score表记录;

    • 查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;

    • 查询每门课程被选修的学生数;

    • 查询出只选修了一门课程的全部学生的学号和姓名;

    • 查询选修“杨艳”老师所授课程的学生中,成绩最高的学生姓名及其成绩;

    • 查询两门以上不及格课程的同学的学号及其平均成绩;

1.select score.student_id,student.sname,count(score.course_id) as courses,sum(num) as t_score from score left join student on score.student_id = student.sid group by score.student_id;

2.select * from teacher where tname like "李%";

3. select B.student_id,student.sname,B.s_num from (select student_id,avg(num) as s_num from score group by student_id having s_num>60) as B
      left join student on B.student_id = student.sid;
       
4.select student.sid,student.sname from student where sid in (select student_id from score where score.num<60 group by student_id);


5.delete from score where course_id in (select cid from course left join teacher on course.teacher_id=teacher.tid where tname='叶平');

6.select score.course_id as 课程ID,course.cname as 课程名,max(num) as 最高分,min(num) as 最低分 from score left join course on score.course_id = course.cid group by course_id;

7.select course.cid as 课程ID,course.cname as 课程名,count(1) as 学生数 from score left join course on score.course_id=course.cid group by score.course_id;

8.select course_id as id,course.cname as 课程,count(1) as 人数 from score left join course on score.course_id=course.cid group by course_id

9.select student.sid as 学号,student.sname as 姓名 from student where sid in (select student_id from score group by student_id having count(1)=1)

10.select student_id as 学号,sname as 姓名,avg(num) as 平均成绩 from score left join student on score.student_id=student.sid where student_id in (select student_id from score where num<60 group by student_id having count(1)>=2) group by student_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)
  6.  

posted @ 2020-02-27 11:42  钺璍  阅读(474)  评论(0)    收藏  举报