设为首页 收藏本站
查看: 579|回复: 0

[经验分享] mysql索引与pymysql模块

[复制链接]

尚未签到

发表于 2018-10-4 06:06:12 | 显示全部楼层 |阅读模式
  一、索引
  1、索引介绍
  一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到复杂的查询操作,通过索引可以加速查询。
  索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。
  索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要。
  2、索引原理
  本质:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
  我们把数据存储在磁盘上,磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分。访问一次磁盘的时间,即一次磁盘IO的时间约等于9ms左右,所以减少磁盘IO是优化的关键。
  3、聚集索引与辅助索引
  在数据库中,B+树的高度一般都在2~4层,这也就是说查找某一个键值的行记录时最多只需要2到4次IO,这倒不错。因为当前一般的机械硬盘每秒至少可以做100次IO,2~4次的IO意味着查询时间只需要0.02~0.04秒。
  数据库中的B+树索引可以分为聚集索引(clustered index)和辅助索引(secondary index)
  聚集索引与辅助索引的对比:
  相同的是:不管是聚集索引还是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着所有的数据。
  不同的是:叶子结点存放的是否是一整行的信息
  (1)聚集索引
  聚集索引的好处之一:它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据。如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录
  聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
  (2)辅助索引
  辅助索引的叶子节点不包含行记录的全部数据。
  叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含一个书签(bookmark)。该书签用来告诉InnoDB存储引擎去哪里可以找到与索引相对应的行数据。
  由于InnoDB存储引擎是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。
  辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引,但只能有一个聚集索引。
  当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶子级别的指针获得只想主键索引的主键,然后再通过主键索引来找到一个完整的行记录。
  4、总结

  (1) 一定是为搜索条件的字段创建索引,比如select * from s1 where>  (2) 在表中已经有大量数据的情况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快
  (3) 需要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI
  5、使用索引
  并不是说我们创建了索引就一定会加快查询速度,若想利用索引达到预想的提高查询速度的效果,我们在添加索引时,必须遵循以下问题:
  (1)范围问题,或者说条件不明确,条件中出现这些符号或关键字:>、>=、 a->b->c的顺序
  # 3、or的工作原理
  条件:
  a = 10 or b = 'xxx' or c > 3 or d = 4
  索引:
  制作联合索引(d, a, b, c)
  工作原理:
  对于连续多个or:mysql会按照条件的顺序,从左到右依次判断,即a->b->c->d
  (6)最左前缀匹配原则,对于组合索引mysql会一直向右匹配直到遇到范围查询(>、 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
  (7)其他
  - 使用函数
  select * from tb1 where reverse(email) = 'egon';
  - 类型不一致
  如果列是字符串类型,传入条件是必须用引号引起来,不然...
  select * from tb1 where email = 999;
  # 排序条件为索引,则select字段必须也是索引字段,否则无法命中
  - order by
  select name from s1 order by email desc;
  当根据索引排序时候,select查询的字段如果不是索引,则速度仍然很慢
  select email from s1 order by email desc;
  特别的:如果对主键排序,则还是速度很快:
  select * from tb1 order by nid desc;
  - 组合索引最左前缀
  如果组合索引为:(name, email)
  name and email - - 命中索引
  name - - 命中索引
  email - - 未命中索引
  - count(1)或count(列)代替count(*) 在mysql中没有差别了
  - create index xxxx on tb(title(19))                # text类型,必须制定长度
  6、注意事项
  - 避免使用select *
  - count(1)或count(列) 代替 count(*)
  - 创建表时尽量时 char 代替 varchar
  - 表的字段顺序固定长度的字段优先
  - 组合索引代替多个单列索引(经常使用多个条件查询时)
  - 尽量使用短索引
  - 使用连接(JOIN)来代替子查询(Sub-Queries)
  - 连表时注意条件类型需一致
  - 索引散列值(重复少)不适合建索引,例:性别不适合
  7、 联合索引
  联合索引时指对表上的多个列合起来做一个索引。联合索引的创建方法与单个索引的创建方法一样,不同之处在仅在于有多个索引列
  mysql> create table t(
  -> a int,
  -> b int,
  -> primary key(a),

  -> key>  -> );
  8、覆盖索引
  InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。
  使用覆盖索引的一个好处是:辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作
  覆盖索引的另外一个好处是对某些统计问题而言的。
  对于(a,b)形式的联合索引,一般是不可以选择b中所谓的查询条件。但如果是统计操作,并且是覆盖索引,则优化器还是会选择使用该索引
  二、慢查询
  1、慢查询优化的基本步骤:
  0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
  1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
  2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
  3.order by limit 形式的sql语句让排序的表优先查
  4.了解业务方使用场景
  5.加索引时参照建索引的几大原则
  6.观察结果,不符合预期继续从0分析
  2、慢日志管理
  慢日志
  - 执行时间 > 10
  - 未命中索引
  - 日志文件路径
  配置:
  - 内存
  show variables like '%query%';
  show variables like '%queries%';
  set global 变量名 = 值
  - 配置文件
  mysqld - -defaults - file = 'E:\mysql-5.7.16-winx64\mysql-5.7.16-winx64\my-default.ini'
  my.conf内容:
  slow_query_log = ON
  slow_query_log_file = E: / ....
  注意:修改配置文件之后,需要重启服务
  3、mysql日志管理
  (1)bin-log
  #1. 启用
  # vim /etc/my.cnf
  [mysqld]
  log-bin[=dir\[filename]]
  # service mysqld restart
  #2. 暂停
  //仅当前会话
  SET SQL_LOG_BIN=0;
  SET SQL_LOG_BIN=1;
  #3. 查看
  查看全部:
  # mysqlbinlog mysql.000002
  按时间:
  # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56"
  # mysqlbinlog mysql.000002 --stop-datetime="2012-12-05 11:02:54"
  # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" --stop-datetime="2012-12-05 11:02:54"
  按字节数:
  # mysqlbinlog mysql.000002 --start-position=260
  # mysqlbinlog mysql.000002 --stop-position=260
  # mysqlbinlog mysql.000002 --start-position=260 --stop-position=930
  #4. 截断bin-log(产生新的bin-log文件)
  a. 重启mysql服务器
  b. # mysql -uroot -p123 -e 'flush logs'
  #5. 删除bin-log文件
  # mysql -uroot -p123 -e 'reset master'
  (2)查询日志
  启用通用查询日志
  # vim /etc/my.cnf
  [mysqld]
  log[=dir\[filename]]
  # service mysqld restart                  #修改配置文件后需要重启服务
  (3)慢查询日志
  启用慢查询日志
  # vim /etc/my.cnf
  [mysqld]
  log-slow-queries[=dir\[filename]]
  long_query_time=n
  # service mysqld restart                   #修改配置文件后需要重启服务
  MySQL 5.6:
  slow-query-log=1
  slow-query-log-file=slow.log
  long_query_time=3
  查看慢查询日志
  测试:BENCHMARK(count,expr)
  SELECT BENCHMARK(50000000,2*3);
三、pymysql模块  pip3 install pymysql       #安装模块
  1、创建表
import pymysql  
conn=pymysql.connect(
  
    host='127.0.0.1',
  
    port=3306,
  
    user='root',
  
    password='',
  
    db='test'
  
)
  
cursor=conn.cursor()       #拿游标
  
sql='''
  
create table info(
  
    id int primary key auto_increment,
  
    username char(16),
  
    password char(20)
  
);
  
'''
  
cursor.execute(sql)        #提交sql
  
conn.commit()
  
cursor.close()           #关闭(游标)
  
conn.close()             #断开连接
  2、插入数据
import pymysql  
conn=pymysql.connect(
  
    host='127.0.0.1',
  
    port=3306,
  
    user='root',
  
    password='',
  
    db='test'
  
)
  
cursor=conn.cursor()       #拿游标
  
# sql='insert into info(username,password) values("root","123456");'          #插入单条数据
  
# cursor.execute(sql)
  

  
sql='insert into info(username,password) values(%s,%s);'
  
res=cursor.executemany(sql,[("root","123456"),("lhf","12356"),("eee","156")])    #插入多条记录,执行sql语句,返回sql影响成功的行数
  
print (res)
  
cursor.close()
  
conn.commit()            #数据的增、删、改必须要提交
  
conn.close()
  3、查询数据操作
import pymysql  
conn=pymysql.connect(
  
    host='127.0.0.1',
  
    port=3306,
  
    user='root',
  
    password='',
  
    db='test'
  
)
  
cursor=conn.cursor(pymysql.cursors.DictCursor)        #数据以字典形式显示
  
# cursor=conn.cursor()                                    #数据以元组形式显示
  
sql='select * from info'
  
rows=cursor.execute(sql)
  
print(cursor.fetchone())             #取出第一条数据 {'id': 1, 'username': 'root', 'password': '123456'}
  
# print(cursor.fetchmany(3))          #可以指定显示数据的条数
  
#print(cursor.fetchall())            #显示所有的数据
  

  
cursor.scroll(0,mode='absolute')        # 相对绝对位置移动
  
print(cursor.fetchone())              #显示第一条数据{'id': 1, 'username': 'root', 'password': '123456'}
  
cursor.scroll(1,mode='relative')        # 相对当前位置移动
  
print(cursor.fetchone())              #显示第三条数据{'id': 3, 'username': 'eee', 'password': '156'}
  
cursor.close()
  
conn.close()
  4、execute()之sql注入
  根本原理:就根据程序的字符串拼接name='%s',我们输入一个xxx' -- haha,用我们输入的xxx加'在程序中拼接成一个判断条件name='xxx' -- haha'
  (1)之前的版本
import pymysql  
user=input('user>>: ').strip()
  
pwd=input('password>>: ').strip()
  
conn=pymysql.connect(
  
    host='127.0.0.1',
  
    port=3306,
  
    user='root',
  
    password='',
  
    db='test'
  
)
  
cursor=conn.cursor(pymysql.cursors.DictCursor)
  
sql='select id from info where username="%s" and password="%s"' %(user,pwd)
  
print(sql)
  
#用户名为:eee"  -- aa  ,密码为空,这时候sql就select * from info where username="eee" -- aa" and password="",登录成功
  
#用户名为:xxx" or 1=1 -- aa ,密码为空,这时候sql就select * from info where username="xxx" or 1=1 -- aa" and password=""登录成功
  
rows=cursor.execute(sql)
  
if rows:
  
    print('登录成功')
  
else:
  
    print('用户名或密码错误')
  
cursor.close()
  
conn.commit()
  
conn.close()
  (2)修改后的版本
import pymysql  
user=input('user>>: ').strip()
  
pwd=input('password>>: ').strip()
  
conn=pymysql.connect(
  
    host='127.0.0.1',
  
    port=3306,
  
    user='root',
  
    password='',
  
    db='test'
  
)
  
cursor=conn.cursor(pymysql.cursors.DictCursor)
  
sql='select * from user where username=%s and password=%s'
  
rows=cursor.execute(sql,(user,pwd))
  
if rows:
  
    print('登录成功')
  
else:
  
    print('用户名或密码错误')
  
cursor.close()
  
conn.commit()
  
conn.close()
  
#修改后,pymysql会处理掉用户输入的特殊字符,就解决了sql的注入问题
  5、获取插入的最后一条数据的自增ID
import pymysql  
conn=pymysql.connect(host='127.0.0.1',user='root',password='',database='test')
  
cursor=conn.cursor()
  
sql='insert into info(name,password) values("xxx","123");'
  
rows=cursor.execute(sql)
  
print(cursor.lastrowid)              #在插入语句后查看
  
conn.commit()
  
cursor.close()
  
conn.close()
  四、视图
  视图是一个虚拟表,只有表结构,没有数据
  使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用
  我们不应该修改视图中的记录,而且在涉及多个表的情况下是根本无法修改视图中的记录的。
  五、触发器
  1、创建触发器
  # 插入前
  CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
  BEGIN
  ...
  END
  # 插入后
  CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
  BEGIN
  ...
  END
  # 删除前
  CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
  BEGIN
  ...
  END
  # 删除后
  CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
  BEGIN
  ...
  END
  # 更新前
  CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
  BEGIN
  ...
  END
  # 更新后
  CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
  BEGIN
  ...
  END
  2、插入后触发触发器
#准备表  
CREATE TABLE cmd (
  
    id INT PRIMARY KEY auto_increment,
  
    USER CHAR (32),
  
    priv CHAR (10),
  
    cmd CHAR (64),
  
    sub_time datetime, #提交时间
  
    success enum ('yes', 'no') #0代表执行失败
  
);
  
CREATE TABLE errlog (
  
    id INT PRIMARY KEY auto_increment,
  
    err_cmd CHAR (64),
  
    err_time datetime
  
);
  
#创建触发器
  
delimiter //                    #定义sql结束符
  
CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
  
BEGIN
  
    IF NEW.success = 'no' THEN #等值判断只有一个等号
  
            INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必须加分号
  
      END IF ;                  #必须加分号
  
END//                            #sql语句结束
  
delimiter ;                     #sql结束符修改为之前的分号
  
#往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
  
INSERT INTO cmd (
  
    USER,
  
    priv,
  
    cmd,
  
    sub_time,
  
    success
  
)
  
VALUES
  
    ('egon','0755','ls -l /etc',NOW(),'yes'),
  
    ('egon','0755','cat /etc/passwd',NOW(),'no'),
  
    ('egon','0755','useradd xxx',NOW(),'no'),
  
    ('egon','0755','ps aux',NOW(),'yes');
  
#查询错误日志,发现有两条
  
mysql> select * from errlog;
  
+----+-----------------+---------------------+
  
| id | err_cmd         | err_time            |
  
+----+-----------------+---------------------+
  
|  1 | cat /etc/passwd | 2017-09-14 22:18:48 |
  
|  2 | useradd xxx     | 2017-09-14 22:18:48 |
  
+----+-----------------+---------------------+
  特别的:NEW表示即将插入的数据行,OLD表示即将删除的数据行。
  #触发器无法由用户直接调用,而是由于对表的【增/删/改】操作被动引发的
  六、事务
  事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
create table user(  
id int primary key auto_increment,
  
name char(32),
  
balance int
  
);
  
insert into user(name,balance)
  
values
  
('wsb',1000),
  
('egon',1000),
  
('ysb',1000);
  
#原子操作
  
start transaction;
  
update user set balance=900 where name='wsb'; #买支付100元
  
update user set balance=1010 where name='egon'; #中介拿走10元
  
update user set balance=1090 where name='ysb'; #卖家拿到90元
  
commit;
  

  
#出现异常,回滚到初始状态
  
比如:卖家应该拿到90元,出现异常而没有拿到90元,需要回到之前的数据
  
rollback;                                      #回滚操作,在数据提交前有效,提交后无法回滚
  
commit;                                         #提交数据
  
mysql> select * from user;
  
+----+------+---------+
  
| id | name | balance |
  
+----+------+---------+
  
|  1 | wsb  |    1000 |
  
|  2 | egon |    1000 |
  
|  3 | ysb  |    1000 |
  
+----+------+---------+
  七、存储过程
  1、存储过程介绍
  存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql
  使用存储过程的优点:
  #1. 用于替代程序写的SQL语句,实现程序与sql解耦
  #2. 基于网络传输,传别名的数据量小,而直接传sql数据量大
  使用存储过程的缺点:
  #1. 程序员扩展功能不方便
  程序与数据库结合使用的三种方式
  #方式一:
  MySQL:存储过程
  程序:调用存储过程
  #方式二:
  MySQL:
  程序:纯SQL语句
  #方式三:
  MySQL:
  程序:类和对象,即ORM(本质还是纯SQL语句)
  2、创建无参的存储过程
delimiter //  
create procedure p1()
  
BEGIN
  
    select * from blog;
  
    INSERT into blog(name,sub_time) values("xxx",now());
  
END //
  
delimiter ;
  
#在mysql中调用
  
call p1()
  
#在python中基于pymysql调用
  
cursor.callproc('p1')
  
print(cursor.fetchall())
  3、创建有参的存储过程
  对于存储过程,可以接收参数,其参数有三类:
  (1)in          #仅用于传入参数用
delimiter //  
create procedure p2( in n1 int, in n2 int )
  
BEGIN
  
    select * from blog where id > n1;
  
END //
  
delimiter;
  
# 在mysql中调用
  
call p2(3, 2)
  
# 在python中基于pymysql调用
  
cursor.callproc('p2', (3, 2))
  
print(cursor.fetchall())
  (2)out        #仅用于返回值用
delimiter //  
create procedure p3(
  
    in n1 int,
  
    out res int
  
)
  
BEGIN
  
    select * from blog where id > n1;
  
    set res = 1;
  
END //
  
delimiter ;
  
#在mysql中调用
  
set @res=0;                             #0代表假(执行失败),1代表真(执行成功)
  
call p3(3,@res);
  
select @res;
  
#在python中基于pymysql调用
  
cursor.callproc('p3',(3,0))               #0相当于set @res=0
  
print(cursor.fetchall())                  #查询select的查询结果
  
cursor.execute('select @_p3_0,@_p3_1;') #@p3_0代表第一个参数,@p3_1代表第二个参数,即返回值
  
print(cursor.fetchall())
  (3)inout        #既可以传入又可以当作返回值
delimiter //  
create procedure p4(
  
    inout n1 int
  
)
  
BEGIN
  
    select * from blog where id > n1;
  
    set n1 = 1;
  
END //
  
delimiter ;
  
#在mysql中调用
  
set @x=3;
  
call p4(@x);
  
select @x;
  
#在python中基于pymysql调用
  
cursor.callproc('p4',(3,))
  
print(cursor.fetchall())                     #查询select的查询结果
  
cursor.execute('select @_p4_0;')
  
print(cursor.fetchall())
  4、事务的应用
delimiter //  
create PROCEDURE p5(OUT p_return_code tinyint)
  
BEGIN
  
    DECLARE exit handler for sqlexception
  
    BEGIN
  
        -- ERROR
  
        set p_return_code = 1;
  
        rollback;
  
    END;
  
    DECLARE exit handler for sqlwarning
  
    BEGIN
  
        -- WARNING
  
        set p_return_code = 2;
  
        rollback;
  
    END;
  
    START TRANSACTION;
  
        DELETE from tb1; #执行失败
  
        insert into blog(name,sub_time) values('yyy',now());
  
    COMMIT;
  
    -- SUCCESS
  
    set p_return_code = 0; #0代表执行成功
  
END //
  
delimiter ;
  
#在mysql中调用存储过程
  
set @res=123;
  
call p5(@res);
  
select @res;
  
#在python中基于pymysql调用存储过程
  
cursor.callproc('p5',(123,))
  
print(cursor.fetchall()) #查询select的查询结果
  
cursor.execute('select @_p5_0;')
  
print(cursor.fetchall())
  5、执行存储过程
#!/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)
  
# 执行存储过程
  
cursor.callproc('p1', args=(1, 22, 3, 4))
  
# 获取执行完存储的参数
  
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
  
result = cursor.fetchall()
  
conn.commit()
  
cursor.close()
  
conn.close()
  
print(result)
  6、函数
  date_format函数
  (1)基本使用
  mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
  -> 'Sunday October 2009'
  mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
  -> '22:23:00'
  mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
  ->                 '%D %y %a %d %m %b %j');
  -> '4th 00 Thu 04 10 Oct 277'
  mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
  ->                 '%H %k %I %r %T %S %w');
  -> '22 22 10 10:23:00 PM 22:23:00 00 6'
  mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
  -> '1998 52'
  mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
  -> '00'
  (2) 准备表和记录
CREATE TABLE blog (  
    id INT PRIMARY KEY auto_increment,
  
    NAME CHAR (32),
  
    sub_time datetime
  
);
  
INSERT INTO blog (NAME, sub_time)
  
VALUES
  
    ('第1篇','2015-03-01 11:31:21'),
  
    ('第2篇','2015-03-11 16:31:21'),
  
    ('第3篇','2016-07-01 10:21:31'),
  
    ('第4篇','2016-07-22 09:23:21'),
  
    ('第5篇','2016-07-23 10:11:11'),
  
    ('第6篇','2016-07-25 11:21:31'),
  
    ('第7篇','2017-03-01 15:33:21'),
  
    ('第8篇','2017-03-01 17:32:21'),
  
    ('第9篇','2017-03-01 18:31:21');
  (3) 提取sub_time字段的值,按照格式后的结果即"年月"来分组
SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m');  
#结果
  
+-------------------------------+----------+
  
| DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) |
  
+-------------------------------+----------+
  
| 2015-03                       |        2 |
  
| 2016-07                       |        4 |
  
| 2017-03                       |        3 |
  
+-------------------------------+----------+



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-611519-1-1.html 上篇帖子: Python开发之MySQL数据库 下篇帖子: MySQL事务隔离级别详解
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表