第八章| 3. MyAQL数据库|Navicat工具与pymysql模块

1、Navicat工具与pymysql模块

在生产环境中操作MySQL数据库还是推荐使用命令行工具mysql,但在我们自己开发测试时,可以使用可视化工具Navicat,以图形界面的形式操作MySQL数据库

掌握:
#1. 测试+链接数据库
#2. 新建库
#3. 新建表,新增字段+类型+约束
#4. 设计表:外键
#5. 新建查询
#6. 备份库/表

#注意:
批量加注释:ctrl+?键
批量去注释:ctrl+shift+?键

之前我们都是通过MySQL自带的命令行客户端工具mysql来操作数据库,那如何在python程序中操作数据库呢?这就用到了pymysql模块,该模块本质就是一个套接字客户端软件,使用前需要事先安装   pip3 install pymysql

import pymysql

user=input('user>>: ').strip()
pwd=input('password>>: ').strip()

# 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db10',
    charset='utf8'
)

# 拿到游标
cursor=conn.cursor()

# 执行sql语句
# sql='select * from userinfo where user = "%s" and pwd="%s"' %(user,pwd)
# print(sql)

sql='select * from userinfo where user = %s and pwd=%s'
rows=cursor.execute(sql,(user,pwd)) #提交给游标执行  execute这个接口拿到的是2 rows in set (0.00 sec) 2那个行数,如果值不为0说明就输对了

cursor.close()
conn.close()

# 进行判断
if rows:
    print('登录成功')
else:
    print('登录失败')
View Code

mysql语句中 -- xfjl ,-- + 空格后边的都给你注释掉了

import pymysql
user=input('用户名: ').strip()
pwd=input('密码: ').strip()

#链接
conn=pymysql.connect(host='localhost',user='root',password='123',database='egon',charset='utf8')
#游标
cursor=conn.cursor() #执行完毕返回的结果集默认以元组显示
#cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)


#执行sql语句
sql='select * from userinfo where name="%s" and password="%s"' %(user,pwd) #注意%s需要加引号
print(sql)
res=cursor.execute(sql) #执行sql语句,返回sql查询成功的记录数目
print(res)

cursor.close()
conn.close()

if res:
    print('登录成功')
else:
    print('登录失败')


打印: #两种执行的错误
user>>: egon" -- xxxhhhh
password>>: 
select * from userinfo where user = "egon" -- xxxhhhh" and pwd=""
登录成功


user>>: xxxx" or 1=1 -- hhhhhaaa
password>>: 
select * from userinfo where user = "xxxx" or 1=1 -- hhhhhaaa" and pwd=""
登录成功
View Code

改正:

import pymysql

user=input('user>>: ').strip()
pwd=input('password>>: ').strip()

# 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db10',
    charset='utf8'
)

# 拿到游标
cursor=conn.cursor()

# 执行sql语句
# sql='select * from userinfo where user = "%s" and pwd="%s"' %(user,pwd)
# print(sql)
# rows=cursor.execute(sql)

sql='select * from userinfo where user = %s and pwd=%s'
#由execute作为拼接,不用你自己去拼接了,在拼接过程中给你过滤掉这种非法操作
rows=cursor.execute(sql,(user,pwd)) #提交给游标执行  execute这个接口拿到的是2 rows in set (0.00 sec) 2那个行数,如果值不为0说明就输对了

cursor.close()
conn.close()

# 进行判断
if rows:
    print('登录成功')
else:
    print('登录失败')
View Code

增加

import pymysql

# 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db10',
    charset='utf8'
)

# 拿游标
cursor=conn.cursor()

# 执行sql
# 增、删、改  对数据的变动
sql='insert into userinfo(user,pwd) values(%s,%s)'
rows=cursor.execute(sql,('wxx','123'))
print(rows)


conn.commit() #必须加上这个
# 关闭
cursor.close()
conn.close()

打印:
View Code
#1、增删改
import pymysql

# 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db10',
    charset='utf8'
)

# 拿游标
cursor=conn.cursor()

# 执行sql
# 增、删、改  对数据的变动
sql='insert into userinfo(user,pwd) values(%s,%s)'
# rows=cursor.execute(sql,('wxx','123'))
# print(rows)
rows=cursor.executemany(sql,[('yxx','123'),('egon1','111'),('egon2','2222')]) #可以插入多条
print(rows)

conn.commit() #必须加上这个
# 关闭
cursor.close()
conn.close()

打印
View Code
#2、查询
import pymysql
# # 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db10',
    charset='utf8'
)

# 拿游标
cursor=conn.cursor(pymysql.cursors.DictCursor) #基于字典形式的游标,不加括号内的是以元组形式

# 执行sql
# 查询
rows=cursor.execute('select * from userinfo;') #把字符串send给服务端,在服务端把这个sql语句执行下,然后把结果丢给客户端
print(rows)
# print(cursor.fetchone()) #代表取一行
# print(cursor.fetchone())
# print(cursor.fetchone())
# print(cursor.fetchone())
# print(cursor.fetchone())
# print(cursor.fetchone())
# print(cursor.fetchone())

#print(cursor.fetchmany(2)) #指定取的个数,以列表的形式

#print(cursor.fetchall()) #拿出所有,列表的形式
# print(cursor.fetchall())

cursor.scroll(3,mode='absolute') #移动光标 相对绝对位置移动,从头数3个开始取出来1个,结果是第4个
#print(cursor.fetchone())
cursor.scroll(2,mode='relative') # 相对当前位置移动 往后跳2位
print(cursor.fetchone())
# 关闭
cursor.close()
conn.close()

打印:
{'id': 6, 'user': 'egon2', 'pwd': '2222'}
View Code
import pymysql
#
# # 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db10',
    charset='utf8'
)
#
# # 拿游标
cursor=conn.cursor()

sql='insert into userinfo(user,pwd) values(%s,%s)'

rows=cursor.executemany(sql,[('egon3','123'),('egon4','111'),('egon5','2222')])
print(cursor.lastrowid) #插入之前光标走到哪里了
#
conn.commit() #必须加上这个
# 关闭
cursor.close()
conn.close()

打印:
View Code

涉及数据库的操作的,先要编写好数据,然后基于pymysql模块帮我把sql语句提交给mysql服户端,执行完之后把结果再返回到应用程序中再做进一步的处理;将应用程序的开发与数据库的开发结合

2、mysql内置函数功能

  视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,可以将该结果集当做表来使用。

  使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用

  视图 只有表结构frm,没有数据,因为是查出来的虚拟表;不用重复写,但是不建议使用,如果有好多个视图,要找到所有的视图给它修改很麻烦;

mysql> use db7;
Database changed
mysql> select * from course;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+
rows in set (0.00 sec)

mysql> select * from teacher;
+-----+-----------------+
| tid | tname           |
+-----+-----------------+
|   1 | 张磊老师        |
|   2 | 李平老师        |
|   3 | 刘海燕老师      |
|   4 | 朱云海老师      |
|   5 | alex            |
+-----+-----------------+
rows in set (0.00 sec)

mysql> select * from course inner join teacher on course.teacher_id=teacher.tid;
+-----+--------+------------+-----+-----------------+
| cid | cname  | teacher_id | tid | tname           |
+-----+--------+------------+-----+-----------------+
|   1 | 生物   |          1 |   1 | 张磊老师        |
|   2 | 物理   |          2 |   2 | 李平老师        |
|   4 | 美术   |          2 |   2 | 李平老师        |
|   3 | 体育   |          3 |   3 | 刘海燕老师      |
+-----+--------+------------+-----+-----------------+
rows in set (0.10 sec)

mysql> create view course2teacher as select * from course inner join teacher on course.teacher_id=teacher.tid;
Query OK, 0 rows affected (0.20 sec)

mysql> show tables;
+----------------+
| Tables_in_db7  |
+----------------+
| course         |
| course2teacher |
| teacher        |
+----------------+
rows in set (0.00 sec)

mysql>
View Code

触发器

使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,注意:没有查询

mysql> create database db11;
Query OK, 1 row affected (0.00 sec)

mysql> use db11;
Database changed
mysql> 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代表执行失败
    -> );
Query OK, 0 rows affected (0.77 sec)

mysql>
mysql> CREATE TABLE errlog (
    ->     id INT PRIMARY KEY auto_increment,
    ->     err_cmd CHAR (64),
    ->     err_time datetime
    -> );
Query OK, 0 rows affected (0.68 sec)

#创建触发器
mysql> delimiter //
mysql> 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//
Query OK, 0 rows affected (0.34 sec)
mysql> delimiter ;
mysql>
mysql>
mysql>#往cmd中插入记录,触动触发器,根据IF条件决定是否插入错误日志
mysql> 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');
Query OK, 4 rows affected (0.43 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from cmd; +----+------+------+-----------------+---------------------+---------+
| id | USER | priv | cmd             | sub_time            | success |
+----+------+------+-----------------+---------------------+---------+
|  1 | egon | 0755 | ls -l /etc      | 2018-04-28 18:26:33 | yes     |
|  2 | egon | 0755 | cat /etc/passwd | 2018-04-28 18:26:33 | no      |
|  3 | egon | 0755 | useradd xxx     | 2018-04-28 18:26:33 | no      |
|  4 | egon | 0755 | ps aux          | 2018-04-28 18:26:33 | yes     |
+----+------+------+-----------------+---------------------+---------+
rows in set (0.00 sec)

mysql> select * from errlog; #查询错误日志发现有两条
+----+-----------------+---------------------+
| id | err_cmd         | err_time            |
+----+-----------------+---------------------+
|  1 | cat /etc/passwd | 2018-04-28 18:26:33 |
|  2 | useradd xxx     | 2018-04-28 18:26:33 |
+----+-----------------+---------------------+
rows in set (0.00 sec)
View Code

存储过程

把mysql处理好的数据给封装好,一个接口名,应用程序可以直接调用接口,这个接口就叫储存过程。是mysql内置功能的一系列总和;

存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql

使用存储过程的优点:

1. 用于替代程序写的SQL语句,实现程序与sql解耦

2. 基于网络传输,传别名的数据量小,而直接传sql数据量大

使用存储过程的缺点:

1. 程序员扩展功能不方便

程序与数据库结合使用的三种方式:

创建简单的存储过程:

#1、无参存储过程
    delimiter //
    create procedure p1()
    BEGIN
        select * from db7.teacher;
    END //
    delimiter ;
mysql> use db7;
Database changed
mysql> delimiter //
mysql> create procedure p1()
    -> BEGIN
    -> select * from db7.teacher;
    -> END //
Query OK, 0 rows affected (0.21 sec)

mysql> delimiter ;
mysql>
mysql>
mysql> show create procedure p1;
+-----------+--------------------------------------------+-----------------------------------------------------------------------------------------+----------------------+----------------------+------
--------------+
| Procedure | sql_mode                                   | Create Procedure                                                                        | character_set_client | collation_connection | Datab
ase Collation |
+-----------+--------------------------------------------+-----------------------------------------------------------------------------------------+----------------------+----------------------+------
--------------+
| p1        | STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION | CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
BEGIN
select * from db7.teacher;
END | utf8                 | utf8_general_ci      | utf8_general_ci    |
+-----------+--------------------------------------------+-----------------------------------------------------------------------------------------+----------------------+----------------------+------
--------------+
row in set (0.00 sec)
View Code
# MySQL中调用
  call p1();  
mysql> call p1();
+-----+-----------------+
| tid | tname           |
+-----+-----------------+
|   1 | 张磊老师        |
|   2 | 李平老师        |
|   3 | 刘海燕老师      |
|   4 | 朱云海老师      |
|   5 | alex            |
+-----+-----------------+
rows in set (0.00 sec)

Query OK, 0 rows affected (0.01 sec)
View Code
# Python中调用
import pymysql

# 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db7',
    charset='utf8'
)

# 拿游标
cursor=conn.cursor()

# 执行sql
cursor.callproc('p1') #只是执行
print(cursor.fetchall())

打印
((1, '张磊老师'), (2, '李平老师'), (3, '刘海燕老师'), (4, '朱云海老师'), (5, 'alex'))
View Code
#2、有参存储过程   #在mysql中参数必须指定类型,是用来接收值的还是返回值的
对于存储过程,可以接收参数,其参数有三类:
#in          仅用于传入参数用
#out        仅用于返回值用
#inout     既可以传入又可以当作返回值
delimiter //
    create procedure p2(in n1 int,in n2 int,out res int) #当存储过程p1执行完了,就把res当做返回值返回了这就是out的作用,只有out的值才能被返回
    BEGIN
        select * from db7.teacher where tid > n1 and tid < n2;
        set res = 1;
    END //
    delimiter ;


    # MySQL中调用
    set @x=0 #初始值等于零
    call p2(2,4,@x);
    select @x;查看结果

    # Python中调用
    cursor.callproc('p2',(2,4,0))# @_p2_0=2,@_p2_1=4,@_p2_2=0
    cursor.execute('select @_p3_2')
    cursor.fetchone()
mysql> delimiter //
mysql> create procedure p2(in n1 int, in n2 int, out res int)
    -> BEGIN
    -> select * from db7.teacher where tid > n1 and tid < n2;
    -> set res = 1;
    -> END //
Query OK, 0 rows affected (0.06 sec)

mysql> delimiter ;

mysql> set @x=0;
Query OK, 0 rows affected (0.00 sec)

mysql> call p2(2,4,@x);
+-----+-----------------+
| tid | tname           |
+-----+-----------------+
|   3 | 刘海燕老师      |
+-----+-----------------+
row in set (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> select @x;
+------+
| @x   |
+------+
|    1 |
+------+
row in set (0.00 sec)
View Code
import pymysql

# 建立链接
conn=pymysql.connect(
    host='192.168.1.123',
    port=3306,
    user='root',
    password='123',
    db='db7',
    charset='utf8'
)

# 拿游标
cursor=conn.cursor()

#
cursor.execute('select @_p2_2')
print(cursor.fetchone())

# 关闭
cursor.close()
conn.close()

打印
(1, )
View Code
应用程序与数据库结合使用
方式一: (很少用,部门之间沟通效率不高;优点很好解开了耦合,效率最高)
    Python:调用存储过程
    MySQL:编写存储过程

    
方式二:
    Python:编写纯生SQL  (可维护性好,都是开发人员写的,)
    MySQL:
    
方式三:
    Python:ORM->纯生SQL (开发效率高,可维护性高,用类,ORM框架)
    MySQL:

事务

同时成功同时失败;

 事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。

mysql> create table user(
    -> id int primary key auto_increment,
    -> name char(32),
    -> balance int
    -> );
Query OK, 0 rows affected (1.98 sec)

mysql>
mysql> insert into user(name,balance)
    -> values
    -> ('wsb',1000),
    -> ('egon',1000),
    -> ('ysb',1000);
Query OK, 3 rows affected (0.50 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | wsb  |    1000 |
|  2 | egon |    1000 |
|  3 | ysb  |    1000 |
+----+------+---------+
rows in set (0.00 sec)

#原子操作
mysql> start transaction;
Query OK, 0 rows affected (0.05 sec)

mysql> update user set balance=900 where name='wsb'; #买支付100元
Query OK, 1 row affected (0.11 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update user set balance=1010 where name='egon'; #中介拿走10元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update user set balance=1090 where name='ysb'; #卖家拿到90元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | wsb  |     900 |
|  2 | egon |    1010 |
|  3 | ysb  |    1090 |
+----+------+---------+
rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.16 sec)

mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | wsb  |     900 |
|  2 | egon |    1010 |
|  3 | ysb  |    1090 |
+----+------+---------+
rows in set (0.00 sec)

mysql> rollback; #出现异常就回滚到初始状态
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | wsb  |     900 |
|  2 | egon |    1010 |
|  3 | ysb  |    1090 |
+----+------+---------+
rows in set (0.00 sec)
View Code

函数与流程控制

函数

一、数学函数
    ROUND(x,y)
        返回参数x的四舍五入的有y位小数的值

    RAND()
        返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。

二、聚合函数(常用于GROUP BY从句的SELECT查询中)
    AVG(col)返回指定列的平均值
    COUNT(col)返回指定列中非NULL值的个数
    MIN(col)返回指定列的最小值
    MAX(col)返回指定列的最大值
    SUM(col)返回指定列的所有值之和
    GROUP_CONCAT(col) 返回由属于一组的列值连接组合而成的结果    

三、字符串函数

    CHAR_LENGTH(str)
        返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
    CONCAT(str1,str2,...)
        字符串拼接
        如有任何一个参数为NULL ,则返回值为 NULL。
    CONCAT_WS(separator,str1,str2,...)
        字符串拼接(自定义连接符)
        CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。

    CONV(N,from_base,to_base)
        进制转换
        例如:
            SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示

    FORMAT(X,D)
        将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若  D 为 0, 则返回结果不带有小数点,或不含小数部分。
        例如:
            SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
    INSERT(str,pos,len,newstr)
        在str的指定位置插入字符串
            pos:要替换位置其实位置
            len:替换的长度
            newstr:新字符串
        特别的:
            如果pos超过原字符串长度,则返回原字符串
            如果len超过原字符串长度,则由新字符串完全替换
    INSTR(str,substr)
        返回字符串 str 中子字符串的第一个出现位置。

    LEFT(str,len)
        返回字符串str 从开始的len位置的子序列字符。

    LOWER(str)
        变小写

    UPPER(str)
        变大写

    REVERSE(str)
        返回字符串 str ,顺序和字符顺序相反。

    SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
        不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。

        mysql> SELECT SUBSTRING('Quadratically',5);
            -> 'ratically'

        mysql> SELECT SUBSTRING('foobarbar' FROM 4);
            -> 'barbar'

        mysql> SELECT SUBSTRING('Quadratically',5,6);
            -> 'ratica'

        mysql> SELECT SUBSTRING('Sakila', -3);
            -> 'ila'

        mysql> SELECT SUBSTRING('Sakila', -5, 3);
            -> 'aki'

        mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
            -> 'ki'

四、日期和时间函数
    CURDATE()或CURRENT_DATE() 返回当前的日期
    CURTIME()或CURRENT_TIME() 返回当前的时间
    DAYOFWEEK(date)   返回date所代表的一星期中的第几天(1~7)
    DAYOFMONTH(date)  返回date是一个月的第几天(1~31)
    DAYOFYEAR(date)   返回date是一年的第几天(1~366)
    DAYNAME(date)   返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE);
    FROM_UNIXTIME(ts,fmt)  根据指定的fmt格式,格式化UNIX时间戳ts
    HOUR(time)   返回time的小时值(0~23)
    MINUTE(time)   返回time的分钟值(0~59)
    MONTH(date)   返回date的月份值(1~12)
    MONTHNAME(date)   返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE);
    NOW()    返回当前的日期和时间
    QUARTER(date)   返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE);
    WEEK(date)   返回日期date为一年中第几周(0~53)
    YEAR(date)   返回日期date的年份(1000~9999)

    重点:
    DATE_FORMAT(date,format) 根据format字符串格式化date值

       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'

五、加密函数
    MD5()    
        计算字符串str的MD5校验和
    PASSWORD(str)   
        返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。

六、控制流函数            
    CASE WHEN[test1] THEN [result1]...ELSE [default] END
        如果testN是真,则返回resultN,否则返回default
    CASE [test] WHEN[val1] THEN [result]...ELSE [default]END  
        如果test和valN相等,则返回resultN,否则返回default

    IF(test,t,f)   
        如果test是真,返回t;否则返回f

    IFNULL(arg1,arg2) 
        如果arg1不是空,返回arg1,否则返回arg2

    NULLIF(arg1,arg2) 
        如果arg1=arg2返回NULL;否则返回arg1
View Code
mysql> CREATE TABLE blog (
    ->     id INT PRIMARY KEY auto_increment,
    ->     NAME CHAR (32),
    ->     sub_time datetime
    -> );
Query OK, 0 rows affected (0.78 sec)

mysql> 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');
Query OK, 9 rows affected (0.14 sec)
Records: 9  Duplicates: 0  Warnings: 0

mysql> 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 |
+-------------------------------+----------+
rows in set (0.22 sec)

mysql>
View Code

流程控制

delimiter //
CREATE PROCEDURE proc_if ()
BEGIN

    declare i int default 0;
    if i = 1 THEN
        SELECT 1;
    ELSEIF i = 2 THEN
        SELECT 2;
    ELSE
        SELECT 7;
    END IF;

END //
delimiter ;

if条件语句
View Code

3、索引原理

索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。

索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。

若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要。

索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等

本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

数据库复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

  当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。

  每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生(B+树是通过二叉查找树,再由平衡二叉树,B树演化而来)。

b+树性质 1.索引字段要尽量的小;2.索引的最左匹配特性

这种数据结构减少I/O次数

真实数据只存在叶子节点的磁盘块的数据项;树杈节点的数据象就是为了建数据结构而虚拟出的;

I/O固定在固定范围,3次,它的高度决定的。

InnoDB存储引擎表示索引组织表,即表中数据按照主键顺序存放,建表的时候一定要建个主键,它会在你这个表里边找主键;

聚集索引与辅助索引

数据库中的B+树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),

聚集索引与辅助索引相同的是:不管是聚集索引还是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着所有的数据。

聚集索引与辅助索引不同的是:叶子结点存放的是否是一整行的信息



猜你喜欢

转载自www.cnblogs.com/bj-mr-li/p/10018840.html