zoukankan      html  css  js  c++  java
  • mysql之子查询、视图、事务及pymysql等

    数据准备

    CREATE TABLE `emp`  ( 
      `id` int(0) NOT NULL AUTO_INCREMENT,
      `name` varchar(10) NOT NULL,
      `gender` enum('男','女','未知') NULL DEFAULT '未知',
      `age` int(0) NULL DEFAULT 0,
      `salary` float NULL DEFAULT 0,
      `area` varchar(20) NULL DEFAULT '中国',
      `port` varchar(20) DEFAULT '未知',
      `dep` varchar(20),
      PRIMARY KEY (`id`)
    );
    
    INSERT INTO `emp` VALUES 
        (1, 'yangsir', '男', 42, 10.5, '上海', '浦东', '市场部'),
        (2, 'engon', '男', 38, 9.4, '山东', '济南', '研发部'),
        (3, 'jerry', '女', 30, 3.0, '江苏', '张家港', '研发部'),
        (4, 'tank', '女', 28, 2.4, '广州', '广东', '研发部'),
        (5, 'jiboy', '男', 28, 2.4, '江苏', '苏州', '研发部'),
        (6, 'zero', '男', 18, 8.8, '中国', '黄浦', '销售部'),
        (7, 'owen', '男', 18, 8.8, '安徽', '宣城', '研发部'),
        (8, 'jason', '男', 28, 9.8, '安徽', '巢湖', '研发部'),
        (9, 'ying', '女', 36, 1.2, '安徽', '芜湖', '销售部'),
        (10, 'kevin', '男', 36, 5.8, '山东', '济南', '研发部'),
        (11, 'monkey', '女', 28, 1.2, '山东', '青岛', '市场部'),
        (12, 'san', '男', 30, 9.0, '上海', '浦东', '销售部'),
        (13, 'san1', '男', 30, 6.0, '上海', '浦东', '销售部'),
        (14, 'san2', '男', 30, 6.0, '上海', '浦西', '研发部'),
        (15, 'ruakei', '女', 67, 2.501, '上海', '陆家嘴', '研发部');
    

    联合分组

    # 数据来源:在单表emp下
    
    # 联合分组:按多个字段综合结果进行分组,其实就是group by 后面跟多个值
    
    # 按 area与port组合后的结果进行分组,只有组合后的结果还一致,才认为是一组
    select group_concat(name),area,port from emp group by area,port;
    #例如 上海-浦东的才算是一组,上海浦西的算一组
    
    select group_concat(name),area from emp group by area;
    # 会把area相同的分成一组,例如 上海的一组,山东的一组
    
    

    子查询

    # 增:insert into 表 select子查询
    # 删:delete from 表 条件是select子查询(表不能与delete表相同)(不能查的同时删除)
    # 查:select 字段 from 表 条件是select子查询
    # 改:update 表 set 字段=值 条件是select子查询(表不能与update表相同)(不能查的时候同时修改)
    
    # 数据来源:在单表emp下
    
    # 子查询:将一条查询sql的结果作为另一条sql的条件
    
    ##############
    ####### 思考:每个部门最高薪资的那个人所有信息##########
    #############
    
    # 子查询的sql
    select dep, max(salary) from emp group by dep;
    # 子查询 - 查
    select * from emp where (dep, salary) in (select dep, max(salary) from emp group by dep);
    #按部门分组,先查最高薪资和部门,在在里面把所有信息打印处理
    
    
    
    # 将子查询转换为一张表
    # 创建一个存子查询数据的一张表
    create table t1(dep_name varchar(64), max_salary decimal(5,2));
    # 子查询 - 增
    insert into t1 select dep, max(salary) from emp group by dep;
    ###insert into 表名 select 子查询语句
    
    # 需求
    select name, dep_name, salary 
    from emp join t1 
    on emp.dep=t1.dep_name and emp.salary=t1.max_salary;
    #把子查询的结果增加到原表中,需要重新创建一个表用于保存子查询表的结果,
    #需要进行连接,按照工资和部门进行join
    
    
    # 子查询 - 改(update更新的表不能 与 子查询select的表同表)
    # 每个部门最大薪资+1
    update t1 set max_salary=max_salary+1;
    # 给t1额外增加一个新部门
    insert into t1 values ('打杂部', 100);
    # 子查询 - 改
    update t1 set max_salary=max_salary+1 where dep_name in (select distinct dep from emp);
    
    
    
    # 错误:update更新的表 与 子查询select的表 相同
    update t1 set max_salary=max_salary+1 where dep_name in (select distinct dep_name from t1);
    
    # 子查询 - 删
    delete from t1 where dep_name in (select distinct dep from emp);
    # 错误: delete删除的表 与 子查询select的表 相同(报错You can't specify target table 't1' for update in FROM clause)
    delete from t1 where dep_name in (select distinct dep_name from t1);
    

    all与any:区间修饰条件

    # 语法规则
    # where id in (1, 2, 3) => id是1或2或3
    # where id not in (1, 2, 3) => id不是1,2,3
    # where salary < all(3, 6, 9) => salary必须小于所有情况(小于最小)
    # where salary > all(3, 6, 9) => salary必须大于所有情况(大于最大)
    # where salary < any(3, 6, 9) => salary只要小于一种情况(小于最大)
    # where salary > any(3, 6, 9) => salary只要大于一种情况(大于最小)
    in < > ()
    # 案例
    select * from emp where salary < all(select salary from emp where id>11);
    

    视图:view

    # 数据依赖:单表emp
    """
    1)视图是存在内存中的临时表
    2)视图的创建依赖select语句,所有就是select语句操作的结果形成的表
    3)视图支持对数据的增删查改 ?
    4)视图不允许对视图表的字段做修改
    5)视图不仅支持创建,也支持更新与删除
    """
    # 语法
    # 创建视图(视图是可以临时存储起来的)
    mysql>: create view 视图名[(别名们)] as select 语句;
    eg>: create view v1 as select dep, max(salary) from emp group by dep;
    #和表结构内容相同
    
    
    # 创建或替换视图
    mysql>: create or replace 视图名[(别名们)] as select 语句;
    #存在就替换,不存在就创建,所以这个命令是常用的
    
    mysql>: alter 视图名[(别名们)] as select 语句;
    
    
    eg>: create or replace view v1(dep_name, max_salary) as select dep, max(salary) from emp group by dep;
    
    
    eg>: alter view v1(name, salary) as select dep, max(salary) from emp group by dep;
    
    # 删除视图
    mysql>: drop view 视图名
    eg>: drop view v1;
    
    # 视图可以作为正常表完成连表查询
    select name, dep_name, salary 
    from emp join v1 
    on emp.dep=v1.dep_name and emp.salary=v1.max_salary;
    #创建视图的时候要改个名字,不然就连不起来
    

    视图的增删改

    # 前提:视图的增删改操作可以直接映射给真实表(本质就是对真实表进行操作)
    
    # 视图可以完成增删改,增删改本质是直接对创建视图的真实表进行操作
    create or replace view v2 as select id,name,age,salary from emp;
    
    update v2 set salary=salary+1 where id=1;
    delete from v2 where id=1;
    
    create or replace view v3 as select * from emp;
    insert into v3 values(1, 'yangsir', '男', 66, 1.11, '上海', '那噶的', '教职部');
    
    # 总结:操作视图,会影响真实表,反之也会影响
    update emp set salary=salary+1 where id=1;
    

    事务

    # 事务:通常一些业务需要多条sql参与,参与的sql会形成一个执行整体,该整体我们就称之为 事务
    
    # 简而言之:事务 - 就是多条执行的sql语句
    
    # 比如:转账就是一个事务:从一个用户将资金转出,再将资金转入到另一个用户
    
    """ 事务的四大特性(必须全部满足)
    
    1.原子性:事务是一组不可分割的单位,要么同时成功,要么同时不成功
    
    
    2.一致性:事务前后的数据完整性应该保持一致(数据库的完整性:如果数据库在某一时间点下,所有的数据都符合所有的约束,则称数据库为完整性的状态)
    
    
    3.隔离性:事务的隔离性是指多个用户并发访问数据时,一个用户的事物不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离
    
    
    4.持久性:持久性是指一个事物一旦被提交,它对数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
    
    
    """
    
    # mysql中事务的执行
    create table bank(
    	id int,
        name varchar(16),
        money decimal(65, 2)
    );
    insert into bank values(1, 'Tom', 10), (2, "Bob", 10);
    
    # 假设出现以下执行情况
    
    # 没有事务支持情况下,Tom的钱就丢了,没有ruakei这个人,所以就转没了
    update bank set money=money-1 where name='Tom';
    update bank set money=money+1 where name='ruakei';
    
    
    # 将两条sql看做事务处理
    # 开启事务
    begin;
    update bank set money=money-1 where name='Tom';
    update bank set money=money+1 where name='ruakei';
    
    # 确认无误,提交事务
    commit;
    
    
    # 确认有误,回滚(相当于撤回,会把数据恢复到操作前的模样)
    rollback;
    

    pymysql:

    python操作mysql

    # 选取操作的模块 pymysql
    # pymysql连接数据库的必要参数:主机、端口、用户名、密码、数据库
    # 注:pymysql不能提供创建数据库的服务,数据库要提前创建
    
    import pymysql
    
    '''
    1)建立数据库连接对象 conn
    2)通过 conn 创建操作sql的 游标对象
    3)编写sql交给 cursor 执行
    4)如果是查询,通过 cursor对象 获取结果
    5)操作完毕,端口操作与连接
    
    '''
    
    # 1. 建立数据库连接对象,参数必须要填写的是用户名、密码、数据库名,且参数名要确定(去源码里面可以看得到),
    
    # autocommint默认是False,为True的话,表示自动变为事务语句
    conn = pymysql.connect(user = 'root', passwd='root', database = 'hupu')
    
    # conn = pymysql.connect(user = 'root', passwd='root', database = 'hupu', autocommit=True)
    
    
    # 2.通过 conn 创建操作sql 的游标对象
    
    # 注:游标不设置参数,查询的结果就是数据元组,数据没有标识性
    
    # 设置pymysql.cursor.DictCursor,查询的结果是字典,key是表的字段
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    # cursor = conn.cursor() #这样的话就是元组,没有键,只有值,所以没有标识性
    
    
    # sql1 = 'create table t1(id int, x int, y int)'
    # cursor.execute(sql1)
    
    
    # 编写sql语句交给 cursor 执行
    
    # 增
    # sql2 = 'insert into t1 values(%s, %s, %s)'
    
    # cursor.executemany(sql2, [(1, 10, 100),(2, 20, 200)])
    # cursor.execute(sql2, (3, 10, 100))
    #只写这句话的话,select * from t1,查不到任何数据,原因是不设置commit,
    # 默认开启事务,增删改操作不会直接映射到数据库中,需要执行 conn.commit()
    # conn.commit()
    #就有数据了
    
    # 删
    # sql3 = 'delete from t1 where id=%s'
    #
    # cursor.execute(sql3, 3)
    # conn.commit()
    
    # 查
    sql5 = 'select * from t1'
    row = cursor.execute(sql5)  # 返回值是受影响的行
    print(row)
    
    # 4)如果是查询,通过 cursor对象 获取结果
    
    '''
    1. fetchone() 偏移一条取出
    2. fetchmany(n) 偏移n条取出
    3. fetchall() 偏移剩余全部
    '''
    r1 = cursor.fetchone() #要和查语句进行连用
    r2 = cursor.fetchone()
    r3 = cursor.fetchmany(2) #紧接着选择两条
    r4 = cursor.fetchall()
    print(r1)
    print(r2)
    print(r3)
    print(r4)
    
    
    # 5)操作完毕,端口操作与连接
    cursor.close()
    conn.close()
    

    游标操作

    import pymysql
    from pymysql.cursors import DictCursor
    
    
    #连接数据库,获取数据库对象conn
    conn = pymysql.connect(user = 'root', passwd = 'root', db='hupu')
    
    #通过 conn 创建操作sql的游标对象
    cursor = conn.cursor(DictCursor)
    
    #编写sql语句,交给cursor游标实现
    sql3 = 'select * from t12'
    
    # sql2 = 'create table t12(id int, x int, y int)'
    # cursor.execute(sql2)
    
    # sql3 = 'insert into t12 values(%s, %s, %s)'
    # cursor.executemany(sql3, [(1, 10, 100), (2, 20, 200), (3, 30, 300), (4, 40, 400)])
    
    
    
    #如果是查询,通过 cursor 对象,获取结果
    row = cursor.execute(sql3)
    if row:
        r1 = cursor.fetchmany(2)
        print(r1)
    
        # 操作游标
        cursor.scroll(2, 'absolute') #绝对路径, 从第一个开始按顺序往下找2个
        # cursor.scroll(1, 'relative') #相对路径,往下1个
        # cursor.scroll(-1, 'relative')
    
    
        r2 = cursor.fetchone()
        print(r2)
    
    # 操作完毕,端口操作与连接
    cursor.close()
    conn.close()
    

    sql注入

    import pymysql
    from pymysql.cursors import DictCursor
    conn = pymysql.connect(user='root', passwd='root', db='hupu')
    cursor = conn.cursor(DictCursor)
    
    
    try:
        sql = 'create table user(id int, name char(4), password char(6))'
        row = cursor.execute(sql)
        print(row)
    except:
        print('表已创建')
        pass
    
    # 空表才插入
    row = cursor.execute('select * from user')
    #就像我们在cmd中创建表一样,创建成功会有提示,几个rows affected(0 rows affected),这里就用于接收这个返回
    
    if not row:
        sql = 'insert into user values(%s,%s,%s)'
        row = cursor.executemany(sql, [(1, 'tom', '123'), (2, 'bob', 'abc')])
        conn.commit()
    
    
    # 用户登录
    usr = input('usr: ')
    pwd = input('pwd: ')
    
    
    
    # 输入用户时:
    #   tom => select * from user where name="tom" and password="%s"
    #   tom" # => select * from user where name="tom" #" and password="%s"
    #                                           (加引号的作用就是把tom加进去了,前面是一段完整的句子,后面全是注释,随便输入就能登录成功)
    # 不自定义用户名时
    #   " or 1=1 # => select * from user where name="" or 1=1 #" and password="%s"
    #                                           (加引号的作用就是把 前面的句子和1=1进行or 逻辑运算
    #                                            这样后者肯定成立,再在后面加# 注释,密码随便输入就可以成功了
    
    
    # sql = 'select * from user where name="%s" and password="%s"' % (usr, pwd) #自己拼接
    # row = cursor.execute(sql)
    # if row:
    #     print('登录成功')
    # else:
    #     print('登录失败')
    
    
    # 由上面的语句可以看出,自己拼接参数一定有sql注入,将数据的占位填充交给pymysql,就能解决sql诸如问题
    sql = 'select * from user where name=%s and password=%s'
    row = cursor.execute(sql, (usr, pwd))
    if row:
        print('登录成功')
    else:
        print('登录失败')
    
    

    事务

    import pymysql
    from pymysql.cursors import DictCursor
    conn = pymysql.connect(user='root', passwd='root', db='hupu')
    cursor = conn.cursor(DictCursor)
    
    #判断表是否创建的异常
    try:
        sql = 'create table t2(id int, name char(4), money int)'
        row = cursor.execute(sql)
        print(row) #创建表,受影响的行是0
    except:
        print('表已创建')
        pass
    
    # 空表才插入
    row = cursor.execute('select * from t2')
    if not row:#如果创建成功,就往里面插入值
        sql = 'insert into t2 values(%s,%s,%s)'
        row = cursor.executemany(sql, [(1, 'tom', 10), (2, 'Bob', 10)])
        conn.commit()
    
    #转给不存在的用户,就会走异常那个流程
    try:
        sql1 = 'update t2 set money=money-1 where name="tom"'
        r1 = cursor.execute(sql1)
        sql2 = 'update t2 set money=money+1 where name="ruakei"'  # 转入的人不存在
        r2 = cursor.execute(sql2)
    except:
        print('转账执行异常')
        conn.rollback() #转账存在异常的话就要进行回滚了
    else:
        print('转账没有异常')
        if r1 == 1 and r2 == 1:
            print('转账成功')
            conn.commit()
        else:
            conn.rollback()
    

    索引

    # 索引就是 键 - key
    
    """
    1)键 是添加给数据库表的 字段 的
    
    2)给表创建 键 后,该表不仅会形成
     表结构、表数据,还有 键的B+结构图
     
    3)键的结构图是需要维护的,在数据完成
     增、删、改操作时,只要影响到有键的字段,
     结构图都要维护一次
     所以创建键后一定会降低 增、删、改 的效率
     
    4)键可以极大的加快查询速度(开发需求中,几
     乎业务都和查有关系)
     
    5)建立键的方式:主键、外键、唯一键、index
    """
    import pymysql
    from pymysql.cursors import DictCursor
    conn = pymysql.connect(user='root', passwd='root', db='hupu')
    cursor = conn.cursor(DictCursor)
    
    # # 创建两张表
    # sql1 = """create table a1(
    #     id int primary key auto_increment,
    #     x int,
    #     y int
    # )"""
    # cursor.execute(sql1)
    # sql2 = """create table a2(
    #     id int primary key auto_increment,
    #     x int,
    #     y int,
    #     index(x)
    # )"""
    # cursor.execute(sql2)
    #
    # # 每个表插入5000条数据
    # import random
    # for i in range(1, 5001):
    #     x = i
    #     y = random.randint(1000, 5000)
    #     cursor.execute('insert into a1(x, y) values(%s, %s)'%(x, y))
    #     cursor.execute('insert into a2(x, y) values(%s, %s)'% (x, y))
    # conn.commit()
    import time
    # a1的x、a1的id、a2的x
    b_time = time.time()
    sql = 'select * from a1 where id=4975'
    cursor.execute(sql)
    e_time = time.time()
    print(e_time - b_time)
    #  0.0010004043579101562
    
    b_time = time.time()
    sql = 'select * from a1 where x=4975'
    cursor.execute(sql)
    e_time = time.time()
    print(e_time - b_time)
    #  0.0019974708557128906
    
    b_time = time.time()
    sql = 'select * from a2 where x=4975'
    cursor.execute(sql)
    e_time = time.time()
    print(e_time - b_time)
    # 0.0009984970092773438
    
  • 相关阅读:
    Vim自动补全神器YouCompleteMe的配置
    sudo apt-get update 时出现的hit、ign、get的含义
    C语言可变参数宏及‘##’在可变参数中的作用
    搬书游戏
    程序中数字取反结果公式
    利用宏定义中的##实现函数模板的作用
    C中的空宏定义,即只有一个参数
    浏览器缓存技术介绍
    Vue相关原理以及手写一个MyVue
    详解JS中的prototype、__proto__与constructor(图解)
  • 原文地址:https://www.cnblogs.com/michealjy/p/11594959.html
Copyright © 2011-2022 走看看