• mysql多表查询


    目录

       1.多表查询 => 转化为一张联合大表

       2.可视化工具
       3.pymysql模块

    多表数据

    create table dep(
        id int primary key auto_increment,
        name varchar(16),
        work varchar(16)
    );
    create table emp(
        id int primary key auto_increment,
        name varchar(16),
        salary float,
        dep_id int
    );
    insert into dep values(1, '市场部', '销售'), (2, '教学部', '授课'), (3, '管理部', '开车');
    insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1),('yingjie', 1.2, 0);
     

    一、笛卡尔积 (交叉连接)

    # 需求: 
    # 查看每位员工的部门的所有信息
    select * from emp;
    select * from dep;
    ​
    # 子查询, 最终结果只能显示单表的信息, 但需求是同时显示两张表的信息 => 先将两张表合成一张表
    select * from emp where dep_id in (select id from dep);
    ​
    笛卡尔积: 集合 X{a, b} * Y{o, p, q} => Z{{a, o}, {a, p}, {a, q}, {b, o}, {b, p}, {b, q}}
    交叉查询: select * from emp, dep; | select * from emp course join dep;
    +----+----------+--------+--------+----+-----------+--------+
    | id | name     | salary | dep_id | id | name      | work   |
    +----+----------+--------+--------+----+-----------+--------+
    |  1 | egon     |      3 |      2 |  1 | 市场部     | 销售   |
    |  1 | egon     |      3 |      2 |  2 | 教学部     | 授课   |
    |  1 | egon     |      3 |      2 |  3 | 管理部     | 开车   |
    |  2 | yanghuhu |      2 |      2 |  1 | 市场部     | 销售   |
    |  2 | yanghuhu |      2 |      2 |  2 | 教学部     | 授课   |
    |  2 | yanghuhu |      2 |      2 |  3 | 管理部     | 开车   |
    |  3 | sanjiang |     10 |      1 |  1 | 市场部     | 销售   |
    |  3 | sanjiang |     10 |      1 |  2 | 教学部     | 授课   |
    |  3 | sanjiang |     10 |      1 |  3 | 管理部     | 开车   |
    |  4 | owen     |  88888 |      2 |  1 | 市场部     | 销售   |
    |  4 | owen     |  88888 |      2 |  2 | 教学部     | 授课   |
    |  4 | owen     |  88888 |      2 |  3 | 管理部     | 开车   |
    |  5 | liujie   |      8 |      1 |  1 | 市场部     | 销售   |
    |  5 | liujie   |      8 |      1 |  2 | 教学部     | 授课   |
    |  5 | liujie   |      8 |      1 |  3 | 管理部     | 开车   |
    |  6 | yingjie  |    1.2 |      0 |  1 | 市场部     | 销售   |
    |  6 | yingjie  |    1.2 |      0 |  2 | 教学部     | 授课   |
    |  6 | yingjie  |    1.2 |      0 |  3 | 管理部     | 开车   |
    +----+----------+--------+--------+----+-----------+--------+
        
    做了筛选, 结果<=完整数据, 非笛卡尔积
    select * from emp, dep where db2.emp.dep_id = db2.dep.id;  # 同sql语句上表现是从两张表拿数据
    +----+----------+--------+--------+----+-----------+--------+
    | id | name     | salary | dep_id | id | name      | work   |
    +----+----------+--------+--------+----+-----------+--------+
    |  1 | egon     |      3 |      2 |  2 | 教学部     | 授课    |
    |  2 | yanghuhu |      2 |      2 |  2 | 教学部     | 授课    |
    |  3 | sanjiang |     10 |      1 |  1 | 市场部     | 销售    |
    |  4 | owen     |  88888 |      2 |  2 | 教学部     | 授课    |
    |  5 | liujie   |      8 |      1 |  1 | 市场部     | 销售    |
    +----+----------+--------+--------+----+-----------+--------+
    # 注意: 同时查询两张表形成新的表,可以称之为虚拟表, 原表与表之间可能存在重复字段, 同时使用时需要明确所属表,必要时还需明确所属数据库
     

    二、多表连接(*****) => 虚拟的单表

    1、内连接

    inner join on
    内连接:结果为两张表有对应关系的数据(emp有dep没有,emp没有dep有的记录均不会被虚拟表展示)
    语法:左表 inner join 右表 on 两表有关联的字段的条件, on就是产生对于关系的(连接的依据)
    eg:select * from emp inner join dep on emp.dep_id = dep.id;
    +----+----------+--------+--------+----+-----------+--------+
    | id | name     | salary | dep_id | id | name      | work   |
    +----+----------+--------+--------+----+-----------+--------+
    |  1 | egon     |      3 |      2 |  2 | 教学部     | 授课    |
    |  2 | yanghuhu |      2 |      2 |  2 | 教学部     | 授课    |
    |  3 | sanjiang |     10 |      1 |  1 | 市场部     | 销售    |
    |  4 | owen     |  88888 |      2 |  2 | 教学部     | 授课    |
    |  5 | liujie   |      8 |      1 |  1 | 市场部     | 销售    |
    +----+----------+--------+--------+----+-----------+--------+
     

    2、左连接

    left join on
    左连接:在内连接的基础上还保留左表特有的记录
    语法:左表 left join 右表 on 两表有关联的字段的条件
    ​
    eg:select emp.name '员工', dep.name '部门', dep.work '职责' from emp left join dep on emp.dep_id = dep.id;
    +----------+-----------+--------+
    | 员工      | 部门      | 职责    |
    +----------+-----------+--------+
    | sanjiang | 市场部     | 销售   |
    | liujie   | 市场部     | 销售   |
    | egon     | 教学部     | 授课   |
    | yanghuhu | 教学部     | 授课   |
    | owen     | 教学部     | 授课   |
    | yingjie  | NULL      | NULL   |
    +----------+-----------+--------+

     

    3、右连接

    right join on
    右连接:在内连接的基础上还保留右表特有的记录
    语法:左表 right join 右表 on 两表有关联的字段的条件
    eg:select * from emp right join dep on emp.dep_id = dep.id;
    +------+----------+--------+--------+----+-----------+--------+
    | id   | name     | salary | dep_id | id | name      | work   |
    +------+----------+--------+--------+----+-----------+--------+
    |    1 | egon     |      3 |      2 |  2 | 教学部     | 授课   |
    |    2 | yanghuhu |      2 |      2 |  2 | 教学部     | 授课   |
    |    3 | sanjiang |     10 |      1 |  1 | 市场部     | 销售   |
    |    4 | owen     |  88888 |      2 |  2 | 教学部     | 授课   |
    |    5 | liujie   |      8 |      1 |  1 | 市场部     | 销售   |
    | NULL | NULL     |   NULL |   NULL |  3 | 管理部     | 开车   |
    +------+----------+--------+--------+----+-----------+--------+
    
    
    在连接语法join 前就是左表, 后就是右表
    采用的是left关键词就是左连接, right关键词就是右连接, inner关键词就是内连接

    4、全连接

    全连接:在内连接的基础上分别保留这左表及右表特有的记录
    语法:mysql没有full join on语法,但可以通过去重达到效果
    eg:
    select * from emp left join dep on emp.dep_id = dep.id
    union
    select * from emp right join dep on emp.dep_id = dep.id;
    +------+----------+--------+--------+------+-----------+--------+
    | id   | name     | salary | dep_id | id   | name      | work   |
    +------+----------+--------+--------+------+-----------+--------+
    |    1 | egon     |      3 |      2 |    2 | 教学部     | 授课    |
    |    2 | yanghuhu |      2 |      2 |    2 | 教学部     | 授课    |
    |    3 | sanjiang |     10 |      1 |    1 | 市场部     | 销售    |
    |    4 | owen     |  88888 |      2 |    2 | 教学部     | 授课    |
    |    5 | liujie   |      8 |      1 |    1 | 市场部     | 销售    |
    | NULL | NULL     |   NULL |   NULL |    3 | 管理部     | 开车    |
    |    6 | yingjie  |    1.2 |      0 | NULL | NULL      | NULL   |
    +------+----------+--------+--------+------+-----------+--------+

     

    练习

    1.查询每一位员工对应的工作职责
    # 每一位员工 => 左表为emp表, 那么左表的所有数据均需要被保留, 所有采用左连接
               => 左表为dep表, 那么右表的所有数据均需要被保留, 所有采用右连接
    # select emp.name, dep.work from emp left join dep on emp.dep_id = dep.id;
    select emp.name, dep.work from dep right join emp on emp.dep_id = dep.id;
    +----------+--------+
    | name     | work   |
    +----------+--------+
    | sanjiang | 销售   |
    | liujie   | 销售   |
    | egon     | 授课   |
    | yanghuhu | 授课   |
    | owen     | 授课   |
    | yingjie  | NULL   |
    +----------+--------+2.查询每一个部门下的员工们及员工职责
    # select max(dep.name), max(dep.work), group_concat(emp.name) from emp right join dep on emp.dep_id = dep.id group by dep_id;
    +---------------+---------------+------------------------+
    | max(dep.name) | max(dep.work) | group_concat(emp.name) |
    +---------------+---------------+------------------------+
    | 管理部         | 开车          | NULL                   |
    | 市场部         | 销售          | liujie,sanjiang        |
    | 教学部         | 授课          | yanghuhu,owen,egon     |
    +---------------+---------------+------------------------+# 分析过程
    # 每一个部门 => dep的信息要被全部保留, 需要分组
    # 员工职责 => dep.work, 由于分组不能直接被查询 => 需要用聚合函数处理
    # 员工们 => emp.name做拼接 => group_concat(emp.name)
    # 分组的字段 => 部门 => emp.dep_id => emp.dep_id可以直接被查询,但没有显示意义 => dep.name用来显示 => dep.name需要用聚合函数处理
    ​
    ​
    select max(dep.name), max(dep.work), group_concat(emp.name) from dep left join emp on  emp.dep_id = dep.id group by emp.dep_id;
    ​
    # 注: on在where条件关键词之左

    三、pymysql模块

    1.基本操作:连接 -> 设置游标 -> 执行sql -> 提交或获取结果 -> 关闭游标与连接
    2.游标设置:pymysql.cursors.DictCursor
    3.游标取值与偏移:fetchone() | fetchmany(n) | scroll(n,'relative|absolute')
    4.防注入:cursor.execute('select * from user where usr=%s and pwd=%s', (usr, pwd))
    5.提交操作结果:conn.commit()
    ​
    # 模块pymysql
    # 按照并导入pymysql: pip3 insatll pymysql
    # 通过pymysql操作数据库分四步:
    1.建立连接
    conn = pymysql.connect(host="localhost", port=3306, db='db2', user='root', password='root')
    ​
    2.设置字典类型游标
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    ​
    3.执行sql语句并使用执行结果
    ​
    # 书写sql语句
    sql = 'select * from emp'
    # 执行sql语句, 有返回值, 返回值为得到的记录行数
    line = cursor.execute(sql)
    print(line)
    ​
    # 使用执行的结果: 
            fetchone())当前游标往后获取一行记录 
            fetchall()当前游标往后所有的记录
            scroll(num, mode="relative|absolute")
                    relative: 游标从当前位置往后移动num行
                    ablolute: 游标从头往后移动num行, 一般可以结合line来使用能定位到任意位置
    tag = cursor.fetchone() # 第一条
    print(tag)
    print(tag['salary'])
    tag = cursor.fetchone() # 第二条
    print(tag)
    cursor.scroll(1, mode='relative') # 偏移第三条
    # cursor.scroll(line - 1, mode='absolute') # 指针绝对, 游标永远从头开始偏移
    tags = cursor.fetchall() # 第四条到最后
    print(tags)
    ​
    4.断开连接
    cursor.close()
    conn.close()

    1、pymysql处理了sql注入

    # 什么是sql注入:
    # 通过书写sql包含(注释相关的)特殊字符, 让原有的sql执行顺序发生改变, 从而改变执行得到的sql
    # 目的:
    # 绕过原有的sql安全认证, 达到对数据库攻击的目的
    # 没有处理sql注入的写法
    sql = 'select * from user where usr="%s" and pwd="%s"' % (usr, pwd)
    res = cursor.execute(sql)
    ​
    # sql注入
    # 1.知道用户名:  abc" -- hehe | ooo
    # select * from user where usr="abc" -- hehe" and pwd="ooo"
    # 2.不知道用户名 aaa" or 1=1 -- hehe | 000
    # select * from user where usr="aaa" or 1=1 -- hehe" and pwd="000"
    # 处理sql注入
    sql = 'select * from user where usr=%s and pwd=%s'
    res = cursor.execute(sql, (usr, pwd))

     

    2、pymysql中增删改

    #
    # 增sql语句
    sql1 = 'insert into user(usr, pwd) values (%s, %s)'
    # 在内存中一次插入一条
    cursor.execute(sql1, ("opq", "123"))
    # 在内存中一次插入多条
    cursor.executemany(sql1, [("aaa", "000"), ("bbb", "111")])
    # 将内存中的数据提交到硬盘中
    conn.commit()

     

  • 相关阅读:
    CodeForces 156B Suspects(枚举)
    CodeForces 156A Message(暴力)
    CodeForces 157B Trace
    CodeForces 157A Game Outcome
    HDU 3578 Greedy Tino(双塔DP)
    POJ 2609 Ferry Loading(双塔DP)
    Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
    Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
    Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
    Java 第十一届 蓝桥杯 省模拟赛十六进制转换成十进制
  • 原文地址:https://www.cnblogs.com/peng-zhao/p/10257135.html
Copyright © 2020-2023  润新知