zoukankan      html  css  js  c++  java
  • pymysql模块

    PyMySQL

    • PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,Python2中则使用mysqldb。
    • Django中也可以使用PyMySQL连接MySQL数据库。
    • PyMySQL安装:pip install pymysql
    #pip3 install pymysql
    import pymysql
     
    user=input('user>>: ').strip()
    pwd=input('password>>: ').strip()
     
    # 建立链接
    conn=pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8'
    )
     
    # 拿到游标
    cursor=conn.cursor() #执行完毕返回的结果集默认以元组显示
    #cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)  #
     
    # sql注入之:用户不存在,绕过用户与密码 aaa' or 1=1 -- 任意字符
    # sql='select * from userinfo where user = "%s" and pwd="%s"' %(user,pwd)
    # print(sql)
     
    #改写为(execute帮我们做字符串拼接,我们无需且一定不能再为%s加引号了)因为pymysql会自动为我们加上,pymysql模块自动帮我们解决sql注入的问题
    sql='select * from userinfo where user = %s and pwd=%s'
    rows=cursor.execute(sql,(user,pwd)) #执行sql语句,返回sql查询成功的记录数目
     
    # print(cursor.fetchone())
    # print(cursor.fetchall())
    # print(cursor.fetchmany(2))   #一般不用直接用limit控制数量
     
    # cursor.scroll(3,mode='absolute') # 相对绝对位置移动
    # print(cursor.fetchone())
    # cursor.scroll(2,mode='relative') # 相对当前位置移动
    # print(cursor.fetchone())
     
    # 增、删、改 需要提交:conn.commit()  #提交后表中记录才会变动
    sql99='insert into userinfo(user,pwd) values(%s,%s)'
     
    rows99=cursor.execute(sql99,('tom','123'))#单条
    print(rows99)
     
    rows99=cursor.executemany(sql99,[('jack','123'),('rose','111'),('tony','2222')])#多条
    print(rows99)
     
    print(cursor.lastrowid)# 获取插入的最后一条数据的自增ID
     
    conn.commit()# 增、删、改 需要提交:conn.commit()  #提交后表中记录才会变动
     
    cursor.close()
    conn.close()
     
    # 进行判断
    if rows:
        print('登录成功')
    else:
        print('登录失败')
     
    """
    按年月分组查询
    select  date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m')
    """
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    # 得到一个可以执行SQL语句并且将结果作为字典返回的游标
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # 定义要执行的SQL语句
    sql = """
    CREATE TABLE USER1 (
    id INT auto_increment PRIMARY KEY ,
    name CHAR(10) NOT NULL UNIQUE,
    age TINYINT NOT NULL
    )ENGINE=innodb DEFAULT CHARSET=utf8;
    """
    # 执行SQL语句
    cursor.execute(sql)
    # 关闭光标对象
    cursor.close()
    # 关闭数据库连接
    conn.close()
    
    ##注意: charset=“utf8”,编码不要写成"utf-8"
    
    #################### 增 ####################
    
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    cursor = conn.cursor()# 得到一个可以执行SQL语句的光标对象
    sql = "INSERT INTO USER1(name, age) VALUES (%s, %s);"
    username = "Alex"
    age = 18
    # 执行SQL语句
    cursor.execute(sql, [username, age])
    # 提交事务
    conn.commit()
    cursor.close()
    conn.close()
    
    #################### 回滚 rollback()取消操作 ####################
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    sql = "INSERT INTO USER1(name, age) VALUES (%s, %s);"
    username = "Alex"
    age = 18
    try:
        # 执行SQL语句
        cursor.execute(sql, [username, age])
        # 提交事务
        conn.commit()
    except Exception as e:
        # 有异常,回滚事务
        conn.rollback()
    cursor.close()
    conn.close()
    
    #################### 获取插入数据的ID(关联操作时会用到) ####################
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    sql = "INSERT INTO USER1(name, age) VALUES (%s, %s);"
    username = "Alex"
    age = 18
    try:
        # 执行SQL语句
        cursor.execute(sql, [username, age])
        # 提交事务
        conn.commit()
        # 提交之后,获取刚插入的数据的ID
        last_id = cursor.lastrowid
    except Exception as e:
        # 有异常,回滚事务
        conn.rollback()
    cursor.close()
    conn.close()
    
    #################### 批量执行 ####################
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    sql = "INSERT INTO USER1(name, age) VALUES (%s, %s);"
    data = [("Alex", 18), ("Egon", 20), ("Yuan", 21)]
    try:
        # 批量执行多条插入SQL语句
        cursor.executemany(sql, data)
        # 提交事务
        conn.commit()
    except Exception as e:
        # 有异常,回滚事务
        conn.rollback()
    cursor.close()
    conn.close()
    
    #################### 删 ####################
    
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    sql = "DELETE FROM USER1 WHERE id=%s;"
    try:
        cursor.execute(sql, [4])
        # 提交事务
        conn.commit()
    except Exception as e:
        # 有异常,回滚事务
        conn.rollback()
    cursor.close()
    conn.close()
    
    #################### 改 ####################
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    # 修改数据的SQL语句
    sql = "UPDATE USER1 SET age=%s WHERE name=%s;"
    username = "Alex"
    age = 80
    try:
        # 执行SQL语句
        cursor.execute(sql, [age, username])
        # 提交事务
        conn.commit()
    except Exception as e:
        # 有异常,回滚事务
        conn.rollback()
    cursor.close()
    conn.close()
    
    #################### 查询单条数据 ####################
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    # 查询数据的SQL语句
    sql = "SELECT id,name,age from USER1 WHERE id=1;"
    # 执行SQL语句
    cursor.execute(sql)
    # 获取单条查询数据
    ret = cursor.fetchone()
    cursor.close()
    conn.close()
    # 打印下查询结果
    print(ret)
    
    
    #################### 查询多条数据 ####################
    
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host='192.168.1.16',
        port=3306,
        user='root',
        password='123',
        db='db1',
        charset='utf8')
    
    # 得到一个可以执行SQL语句的光标对象
    cursor = conn.cursor()
    # 查询数据的SQL语句
    sql = "SELECT id,name,age from USER1;"
    # 执行SQL语句
    cursor.execute(sql)
    # 获取多条查询数据
    ret = cursor.fetchall()
    cursor.close()
    conn.close()
    # 打印下查询结果
    print(ret)
    
    #################### 进阶用法 ####################
    
    # 可以获取指定数量的数据
    cursor.fetchmany(3)
    # 光标按绝对位置移动1
    cursor.scroll(1, mode="absolute")
    # 光标按照相对位置(当前位置)移动1
    cursor.scroll(1, mode="relative")
    View Code
    数据库中加锁
    from django.db import transaction
    flag = False
    with transaction.atomic():  # 事务
        # 在数据库中加锁
        origin_queryset = models.Customer.objects.filter(id__in=pk_list, status=2,
                                                            consultant__isnull=True).select_for_update() 
        if len(origin_queryset) == len(pk_list):
            models.Customer.objects.filter(id__in=pk_list, status=2,
                                            consultant__isnull=True).update(consultant_id=current_user_id)
            flag = True
    
    if not flag:
        return HttpResponse('手速太慢了,选中的客户已被其他人申请,请重新选择')
    
        # 数据库中:
    start transaction;
    update user set balance=900 where name='rose'; #买支付100元
    update user set balance=1010 where name='tom'; #中介拿走10元
    uppdate user set balance=1090 where name='jack'; #卖家拿到90元,出现异常没有拿到
    rollback;
    commit;
    
        # 事务操作
    with transaction.atomic():
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                                    parent_comment_id=pid)
        models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
        #批量操作
    book_list=[]
    for i in range(100):
        book=Book(title="book_%s"%i,price=i*i)
        book_list.append(book)
    
    Book.objects.bulk_create(book_list,batch_size=50)    
    View Code

    Python操作MySQL主要使用两种方式:

    • 原生模块 pymsql
    • ORM框架 SQLAchemy

    pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同。

    # 下载安装 pip3 install pymysql
    
    # 1、执行SQL
    
    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
    
    # 创建连接
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    # 创建游标
    cursor = conn.cursor()
    
    # 执行SQL,并返回收影响行数
    effect_row = cursor.execute("update hosts set host = '1.1.1.2'")
    
    # 执行SQL,并返回受影响行数
    # effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
    
    # 执行SQL,并返回受影响行数
    # effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
    
    
    # 提交,不然无法保存新建或者修改的数据
    conn.commit()
    
    # 关闭游标
    cursor.close()
    # 关闭连接
    conn.close()
    
    
    #2、获取新创建数据自增ID
    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
    
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    cursor = conn.cursor()
    cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11", 1), ("1.1.1.11", 2)])
    conn.commit()
    cursor.close()
    conn.close()
    
    # 获取最新自增ID
    new_id = cursor.lastrowid
    
    
    #3、获取查询数据
    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
    
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    cursor = conn.cursor()
    cursor.execute("select * from hosts")
    
    # 获取第一行数据
    row_1 = cursor.fetchone()
    
    # 获取前n行数据
    # row_2 = cursor.fetchmany(3)
    # 获取所有数据
    # row_3 = cursor.fetchall()
    
    conn.commit()
    cursor.close()
    conn.close()
    
    #
    # 注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:
    #
    # cursor.scroll(1,mode='relative')  # 相对当前位置移动
    # cursor.scroll(2,mode='absolute') # 相对绝对位置移动
    
    
    #
    # 4、fetch数据类型
    #
    #   关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:
    
    
    
    
    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
    
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    
    # 游标设置为字典类型
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    r = cursor.execute("call p1()")
    
    result = cursor.fetchone()
    
    conn.commit()
    cursor.close()
    conn.close()
    使用操作

    SQLAchemy

    SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

    安装:pip3 install SQLAlchemy

    SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

    MySQL-Python
        mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
       
    pymysql
        mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
       
    MySQL-Connector
        mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
       
    cx_Oracle
        oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
       
    更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
    View Code

    一、内部处理

    使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from sqlalchemy import create_engine
      
      
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=5)
      
    # 执行SQL
    # cur = engine.execute(
    #     "INSERT INTO hosts (host, color_id) VALUES ('1.1.1.22', 3)"
    # )
      
    # 新插入行自增ID
    # cur.lastrowid
      
    # 执行SQL
    # cur = engine.execute(
    #     "INSERT INTO hosts (host, color_id) VALUES(%s, %s)",[('1.1.1.22', 3),('1.1.1.221', 3),]
    # )
      
      
    # 执行SQL
    # cur = engine.execute(
    #     "INSERT INTO hosts (host, color_id) VALUES (%(host)s, %(color_id)s)",
    #     host='1.1.1.99', color_id=3
    # )
      
    # 执行SQL
    # cur = engine.execute('select * from hosts')
    # 获取第一行数据
    # cur.fetchone()
    # 获取第n行数据
    # cur.fetchmany(3)
    # 获取所有数据
    # cur.fetchall()
    View Code

    二、ORM功能使用

    使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有组件对数据进行操作。根据类创建对象,对象转换成SQL,执行SQL。

    1、创建表

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
     
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=5)
     
    Base = declarative_base()
     
    # 创建单表
    class Users(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)
        name = Column(String(32))
        extra = Column(String(16))
     
        __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),
            Index('ix_id_name', 'name', 'extra'),
        )
     
     
    # 一对多
    class Favor(Base):
        __tablename__ = 'favor'
        nid = Column(Integer, primary_key=True)
        caption = Column(String(50), default='red', unique=True)
     
     
    class Person(Base):
        __tablename__ = 'person'
        nid = Column(Integer, primary_key=True)
        name = Column(String(32), index=True, nullable=True)
        favor_id = Column(Integer, ForeignKey("favor.nid"))
     
     
    # 多对多
    class Group(Base):
        __tablename__ = 'group'
        id = Column(Integer, primary_key=True)
        name = Column(String(64), unique=True, nullable=False)
        port = Column(Integer, default=22)
     
     
    class Server(Base):
        __tablename__ = 'server'
     
        id = Column(Integer, primary_key=True, autoincrement=True)
        hostname = Column(String(64), unique=True, nullable=False)
     
     
    class ServerToGroup(Base):
        __tablename__ = 'servertogroup'
        nid = Column(Integer, primary_key=True, autoincrement=True)
        server_id = Column(Integer, ForeignKey('server.id'))
        group_id = Column(Integer, ForeignKey('group.id'))
     
     
    def init_db():
        Base.metadata.create_all(engine)
     
     
    def drop_db():
        Base.metadata.drop_all(engine)
    View Code

    注:设置外检的另一种方式 ForeignKeyConstraint(['other_id'], ['othertable.other_id'])

    2、操作表

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=5)
    
    Base = declarative_base()
    
    # 创建单表
    class Users(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)
        name = Column(String(32))
        extra = Column(String(16))
    
        __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),
            Index('ix_id_name', 'name', 'extra'),
        )
    
        def __repr__(self):
            return "%s-%s" %(self.id, self.name)
    
    # 一对多
    class Favor(Base):
        __tablename__ = 'favor'
        nid = Column(Integer, primary_key=True)
        caption = Column(String(50), default='red', unique=True)
    
        def __repr__(self):
            return "%s-%s" %(self.nid, self.caption)
    
    class Person(Base):
        __tablename__ = 'person'
        nid = Column(Integer, primary_key=True)
        name = Column(String(32), index=True, nullable=True)
        favor_id = Column(Integer, ForeignKey("favor.nid"))
        # 与生成表结构无关,仅用于查询方便
        favor = relationship("Favor", backref='pers')
    
    # 多对多
    class ServerToGroup(Base):
        __tablename__ = 'servertogroup'
        nid = Column(Integer, primary_key=True, autoincrement=True)
        server_id = Column(Integer, ForeignKey('server.id'))
        group_id = Column(Integer, ForeignKey('group.id'))
        group = relationship("Group", backref='s2g')
        server = relationship("Server", backref='s2g')
    
    class Group(Base):
        __tablename__ = 'group'
        id = Column(Integer, primary_key=True)
        name = Column(String(64), unique=True, nullable=False)
        port = Column(Integer, default=22)
        # group = relationship('Group',secondary=ServerToGroup,backref='host_list')
    
    
    class Server(Base):
        __tablename__ = 'server'
    
        id = Column(Integer, primary_key=True, autoincrement=True)
        hostname = Column(String(64), unique=True, nullable=False)
    
    
    
    
    def init_db():
        Base.metadata.create_all(engine)
    
    
    def drop_db():
        Base.metadata.drop_all(engine)
    
    
    Session = sessionmaker(bind=engine)
    session = Session()
    表结构 + 数据库连接

    • obj = Users(name="alex0", extra='sb')
      session.add(obj)
      session.add_all([
          Users(name="alex1", extra='sb'),
          Users(name="alex2", extra='sb'),
      ])
      session.commit()
      View Code

    • session.query(Users).filter(Users.id > 2).delete()
      session.commit()
      View Code

    • session.query(Users).filter(Users.id > 2).update({"name" : "099"})
      session.query(Users).filter(Users.id > 2).update({Users.name: Users.name + "099"}, synchronize_session=False)
      session.query(Users).filter(Users.id > 2).update({"num": Users.num + 1}, synchronize_session="evaluate")
      session.commit()
      View Code

    • ret = session.query(Users).all()
      ret = session.query(Users.name, Users.extra).all()
      ret = session.query(Users).filter_by(name='alex').all()
      ret = session.query(Users).filter_by(name='alex').first()
      
      ret = session.query(Users).filter(text("id<:value and name=:name")).params(value=224, name='fred').order_by(User.id).all()
      
      ret = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='ed').all()
      View Code
    • 其他
      View Code

    1、索引
      索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据。对于索引,会保存在额外的文件中。

    2、索引种类

    • 普通索引:仅加速查询
    • 唯一索引:加速查询 + 列值唯一(可以有null)
    • 主键索引:加速查询 + 列值唯一 + 表中只有一个(不可以有null)
    • 组合索引:多列值组成一个索引,
                    专门用于组合搜索,其效率大于索引合并
    • 全文索引:对文本的内容进行分词,进行搜索 

    索引合并,使用多个单列索引组合搜索
    覆盖索引,select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖

    3、相关命令

    - 查看表结构
        desc 表名
     
    - 查看生成表的SQL
        show create table 表名
     
    - 查看索引
        show index from  表名
     
    - 查看执行时间
        set profiling = 1;
        SQL...
        show profiles;

    4、使用索引和不使用索引

    由于索引是专门用于加速搜索而生,所以加上索引之后,查询效率会快到飞起来。
     
    # 有索引
    mysql> select * from tb1 where name = 'wupeiqi-888';
    +-----+-------------+---------------------+----------------------------------+---------------------+
    | nid | name        | email               | radom                            | ctime               |
    +-----+-------------+---------------------+----------------------------------+---------------------+
    | 889 | wupeiqi-888 | wupeiqi888@live.com | 5312269e76a16a90b8a8301d5314204b | 2016-08-03 09:33:35 |
    +-----+-------------+---------------------+----------------------------------+---------------------+
    1 row in set (0.00 sec)
     
    # 无索引
    mysql> select * from tb1 where email = 'wupeiqi888@live.com';
    +-----+-------------+---------------------+----------------------------------+---------------------+
    | nid | name        | email               | radom                            | ctime               |
    +-----+-------------+---------------------+----------------------------------+---------------------+
    | 889 | wupeiqi-888 | wupeiqi888@live.com | 5312269e76a16a90b8a8301d5314204b | 2016-08-03 09:33:35 |
    +-----+-------------+---------------------+----------------------------------+---------------------+
    1 row in set (1.23 sec)

    5、正确使用索引

    数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。
    即使建立索引,索引也不会生效:

    - like '%xx'
        select * from tb1 where name like '%cn';
    - 使用函数
        select * from tb1 where reverse(name) = 'wupeiqi';
    - or
        select * from tb1 where nid = 1 or email = 'seven@live.com';
        特别的:当or条件中有未建立索引的列才失效,以下会走索引
                select * from tb1 where nid = 1 or name = 'seven';
                select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
    - 类型不一致
        如果列是字符串类型,传入条件是必须用引号引起来,不然...
        select * from tb1 where name = 999;
    - !=
        select * from tb1 where name != 'alex'
        特别的:如果是主键,则还是会走索引
            select * from tb1 where nid != 123
    - >
        select * from tb1 where name > 'alex'
        特别的:如果是主键或索引是整数类型,则还是会走索引
            select * from tb1 where nid > 123
            select * from tb1 where num > 123
    - order by
        select email from tb1 order by name desc;
        当根据索引排序时候,选择的映射如果不是索引,则不走索引
        特别的:如果对主键排序,则还是走索引:
            select * from tb1 order by nid desc;
     
    - 组合索引最左前缀
        如果组合索引为:(name,email)
        name and email       -- 使用索引
        name                 -- 使用索引
        email                -- 不使用索引

    6、其他注意事项

    - 避免使用select *
    - count(1)或count(列) 代替 count(*)
    - 创建表时尽量时 char 代替 varchar
    - 表的字段顺序固定长度的字段优先
    - 组合索引代替多个单列索引(经常使用多个条件查询时)
    - 尽量使用短索引
    - 使用连接(JOIN)来代替子查询(Sub-Queries)
    - 连表时注意条件类型需一致
    - 索引散列值(重复少)不适合建索引,例:性别不适合

    7、limit分页

    无论是否有索引,limit分页是一个值得关注的问题

    每页显示10条:
    当前 118 120, 125
    
    倒序:
                大      小
                980    970  7 6  6 5  54  43  32
    
    21 19 98     
    下一页:
    
        select 
            * 
        from 
            tb1 
        where 
            nid < (select nid from (select nid from tb1 where nid < 当前页最小值 order by nid desc limit 每页数据 *【页码-当前页】) A order by A.nid asc limit 1)  
        order by 
            nid desc 
        limit 10;
    
    
    
        select 
            * 
        from 
            tb1 
        where 
            nid < (select nid from (select nid from tb1 where nid < 970  order by nid desc limit 40) A order by A.nid asc limit 1)  
        order by 
            nid desc 
        limit 10;
    
    
    上一页:
    
        select 
            * 
        from 
            tb1 
        where 
            nid < (select nid from (select nid from tb1 where nid > 当前页最大值 order by nid asc limit 每页数据 *【当前页-页码】) A order by A.nid asc limit 1)  
        order by 
            nid desc 
        limit 10;
    
    
        select 
            * 
        from 
            tb1 
        where 
            nid < (select nid from (select nid from tb1 where nid > 980 order by nid asc limit 20) A order by A.nid desc limit 1)  
        order by 
            nid desc 
        limit 10;
    View Code

    8、执行计划

    explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化

    mysql> explain select * from tb2;
    +----+-------------+-------+------+---------------+------+---------+------+------+-------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------+
    |  1 | SIMPLE      | tb2   | ALL  | NULL          | NULL | NULL    | NULL |    2 | NULL  |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------+
    1 row in set (0.00 sec)
        id
            查询顺序标识
                如:mysql> explain select * from (select nid,name from tb1 where nid < 10) as B;
                +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
                | id | select_type | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
                +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
                |  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL    | NULL    | NULL |    9 | NULL        |
                |  2 | DERIVED     | tb1        | range | PRIMARY       | PRIMARY | 8       | NULL |    9 | Using where |
                +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
            特别的:如果使用union连接气值可能为null
    
    
        select_type
            查询类型
                SIMPLE          简单查询
                PRIMARY         最外层查询
                SUBQUERY        映射为子查询
                DERIVED         子查询
                UNION           联合
                UNION RESULT    使用联合的结果
                ...
        table
            正在访问的表名
    
    
        type
            查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
                ALL             全表扫描,对于数据表从头到尾找一遍
                                select * from tb1;
                                特别的:如果有limit限制,则找到之后就不在继续向下扫描
                                       select * from tb1 where email = 'seven@live.com'
                                       select * from tb1 where email = 'seven@live.com' limit 1;
                                       虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就不再继续扫描。
    
                INDEX           全索引扫描,对索引从头到尾找一遍
                                select nid from tb1;
    
                RANGE          对索引列进行范围查找
                                select *  from tb1 where name < 'alex';
                                PS:
                                    between and
                                    in
                                    >   >=  <   <=  操作
                                    注意:!=> 符号
    
    
                INDEX_MERGE     合并索引,使用多个单列索引搜索
                                select *  from tb1 where name = 'alex' or nid in (11,22,33);
    
                REF             根据索引查找一个或多个值
                                select *  from tb1 where name = 'seven';
    
                EQ_REF          连接时使用primary key 或 unique类型
                                select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid;
    
    
    
                CONST           常量
                                表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次。
                                select nid from tb1 where nid = 2 ;
    
                SYSTEM          系统
                                表仅有一行(=系统表)。这是const联接类型的一个特例。
                                select * from (select nid from tb1 where nid = 1) as A;
        possible_keys
            可能使用的索引
    
        key
            真实使用的
    
        key_len
            MySQL中使用索引字节长度
    
        rows
            mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值
    
        extra
            该列包含MySQL解决查询的详细信息
            “Using index”
                此值表示mysql将使用覆盖索引,以避免访问表。不要把覆盖索引和index访问类型弄混了。
            “Using where”
                这意味着mysql服务器将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。
            “Using temporary”
                这意味着mysql在对查询结果排序时会使用一个临时表。
            “Using filesort”
                这意味着mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
            “Range checked for each record(index map: N)”
                这个意味着没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的。
    详细

    更多参见:
      http://www.cnblogs.com/xiaoboluo768/p/5400990.html
      http://dev.mysql.com/doc/refman/5.7/en/explain-output.html#jointype_system

    9、慢日志查询

    a、配置MySQL自动记录慢日志

    slow_query_log = OFF                            是否开启慢日志记录
    long_query_time = 2                              时间限制,超过此时间,则记录
    slow_query_log_file = /usr/slow.log        日志文件
    log_queries_not_using_indexes = OFF     为使用索引的搜索是否记录

    注:查看当前配置信息:
           show variables like '%query%'
         修改当前配置:
        set global 变量名 = 值

    b、查看MySQL慢日志

    mysqldumpslow -s at -a  /usr/local/var/mysql/MacBook-Pro-3-slow.log

    """
    --verbose    版本
    --debug      调试
    --help       帮助
     
    -v           版本
    -d           调试模式
    -s ORDER     排序方式
                 what to sort by (al, at, ar, c, l, r, t), 'at' is default
                  al: average lock time
                  ar: average rows sent
                  at: average query time
                   c: count
                   l: lock time
                   r: rows sent
                   t: query time
    -r           反转顺序,默认文件倒序拍。reverse the sort order (largest last instead of first)
    -t NUM       显示前N条just show the top n queries
    -a           不要将SQL中数字转换成N,字符串转换成S。don't abstract all numbers to N and strings to 'S'
    -n NUM       abstract numbers with at least n digits within names
    -g PATTERN   正则匹配;grep: only consider stmts that include this string
    -h HOSTNAME  mysql机器名或者IP;hostname of db server for *-slow.log filename (can be wildcard),
                 default is '*', i.e. match all
    -i NAME      name of server instance (if using mysql.server startup script)
    -l           总时间中不减去锁定时间;don't subtract lock time from total time
    """
    View Code

      

  • 相关阅读:
    笔记75 微服务笔记2
    笔记73 高级SSM整合2
    笔记72 高级SSM整合
    笔记71 SSM整合
    笔记70 Spring Boot快速入门(八)(重要)
    笔记69 基于Redis的zSet集合做数据缓存实现分页查询
    如何理解多租户架构?
    Tomcat8.0源码编译
    HTML语言
    String类
  • 原文地址:https://www.cnblogs.com/bubu99/p/10230886.html
Copyright © 2011-2022 走看看