MySQL基础知识

参考B站视频资料:https://www.bilibili.com/video/BV1B34y1R7in?p=22&spm_id_from=333.1007.top_right_bar_window_history.content.click

Part0 MySQL常见问题

1.数据库链接错误

MySQL连接时报错:Access denied for user’root’@‘localhost’ (using password: NO) ERROR 1045(28000) 参考博客:https://blog.youkuaiyun.com/fores_t/article/details/92085383

part1:修改my.ini 在[mysqld]下 加入skip-grant-tables

重启mysql服务,可跳过输密码环节;

part2:进入mysql;修改密码

update mysql.user set authentication_string = password(“123456”) where user=“root”;

( 修改密码输入:update mysql.user set authentication_string = password(“新设置的密码”) where user=“root”;(我这里mysql的版本是5.7,其中密码列的属性叫做authentication_string;5.1的是password);

2.导入数据库命令报错

1.中文显示问题(乱码),参考博客:https://www.cnblogs.com/liqiujiong/p/10542326.html

在my.ini的[mysql]下加入default-character-set=utf8,然后重启mysql服务

2.sql语法问题,可以参考对应数据库版本来修改语法

Part1 MySQL数据库

1. 数据库 管理

2.1 内置客户端操作

当连接上MySQL之后,执行如下指令(一般称为SQL语句),就可以对MySQL的数据进行操作。

  • 查看当前所有的数据库: show databases;

  • 创建数据库:create database 数据库名 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

    create database day25db;
    
    create database day25db DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
    
  • 删除数据库:drop database 数据库名;

  • 进入数据(进入文件):use 数据库;

2.2 Python代码操作

无论通过何种方式去连接MySQL,本质上发送的 指令 都是相同的,只是连接的方式和操作形式不同而已。

当连接上MySQL之后,执行如下指令,就可以对MySQL的数据进行操作。(同上述过程)

  • 查看当前所有的数据库 show databases;
  • 创建数据库:create database 数据库名 default charset utf8 collate utf8_general_ci;
  • 删除数据库:drop database 数据库名;
  • 进入数据(进入文件):use 数据库;

想要使用Python操作MySQL需要安装第三方模块:

pip3 install pymysql

安装完成后,就可以编写代码:

import pymysql

# 连接MySQL(socket)
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8")
cursor = conn.cursor()

# 1. 查看数据库
# 发送指令
cursor.execute("show databases")
# 获取指令的结果
result = cursor.fetchall()
print(result) # (('information_schema',), ('mysql',), ('performance_schema',), ('sys',))

# 2. 创建数据库(新增、删除、修改)
# 发送指令
cursor.execute("create database db3 default charset utf8 collate utf8_general_ci")
conn.commit()

# 3. 查看数据库
# 发送指令
cursor.execute("show databases")
# 获取指令的结果
result = cursor.fetchall()
print(result) # (('information_schema',), ('db3',), ('mysql',), ('performance_schema',), ('sys',))

# 4. 删除数据库
# 发送指令
cursor.execute("drop database db3")
conn.commit()

# 3. 查看数据库
# 发送指令
cursor.execute("show databases")
# 获取指令的结果
result = cursor.fetchall()
print(result) # (('information_schema',), ('mysql',), ('performance_schema',), ('sys',))

# 5. 进入数据库,查看表
# 发送指令
cursor.execute("use mysql")
cursor.execute("show tables")
result = cursor.fetchall()
print(result) # (('columns_priv',), ('db',), ('engine_cost',), ('event',), ('func',), ('general_log',),....

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

3. 数据表 管理

3.1 内置客户端操作

数据表常见操作的指令:

  1. 进入数据库 use 数据库;,查看当前所有表:show tables;

  2. 创建表结构
    在这里插入图片描述

create table 表名(
    列名  类型,
    列名  类型,
    列名  类型
)default charset=utf8;

主键一般用于表示当前这条数据的ID编号(类似于人的身份证),需要我们自己来维护一个不重复的值,比较繁琐。所以,在数据库中一般会将主键和自增结合。

create table tb5(
	id int not null auto_increment primary key,	-- 不允许为空 & 主键 & 自增
    name varchar(16) not null,   		-- 不允许为空
    email varchar(32) null,      		-- 允许为空(默认)
    age int default 3            		-- 插入数据时,如果不给age列设置值,默认值:3
)default charset=utf8;

注意:一个表中只能有一个自增列【自增列,一般都是主键】。

  1. 删除表 drop table 表名;

  2. 清空表 delete from 表名;truncate table 表名;(速度快、无法回滚撤销等)

  3. 修改表

  • 添加列

    alter table 表名 add 列名 类型;
    alter table 表名 add 列名 类型 DEFAULT 默认值;
    alter table 表名 add 列名 类型 not null default 默认值;
    alter table 表名 add 列名 类型 not null primary key auto_increment;
    
  • 删除列

    alter table 表名 drop column 列名;
    
  • 修改列 类型

    alter table 表名 modify column 列名 类型;
    
  • 修改列 类型 + 名称

    alter table 表名 change 原列名 新列名 新类型;
    
    alter table  tb change id nid int not null;
    alter table  tb change id id int not null default 5;
    alter table  tb change id id int not null primary key auto_increment;
    
    alter table  tb change id id int; -- 允许为空,删除默认值,删除自增。
    
  • 修改列 默认值

    ALTER TABLE 表名 ALTER 列名 SET DEFAULT 1000;
    
  • 删除列 默认值

    ALTER TABLE 表名 ALTER 列名 DROP DEFAULT;
    
  • 添加主键

    alter table 表名 add primary key(列名);
    
  • 删除主键

    alter table 表名 drop primary key;
    

3.2 数据表常见列类型

create table(
	id int,
    name varchar(16)
)default charset=utf8;
  • int[(m)][unsigned][zerofill]

    int				表示有符号,取值范围:-2147483648 ~ 2147483647
    int unsigned	表示无符号,取值范围:0 ~ 4294967295
    int(5)zerofill	仅用于显示,当不满足5位时,按照左边补0,例如:00002;满足时,正常显示。
    
    mysql> create table L1(id int, uid int unsigned, zid int(5) zerofill) default charset=utf8;
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> insert into L1(id,uid,zid) values(1,2,3);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> insert into L1(id,uid,zid) values(2147483641,4294967294,300000);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from L1;
    +------------+------------+--------+
    | id         | uid        | zid    |
    +------------+------------+--------+
    |          1 |          2 |  00003 |
    | 2147483641 | 4294967294 | 300000 |
    +------------+------------+--------+
    2 rows in set (0.00 sec)
    
    mysql> insert into L1(id,uid,zid) values(214748364100,4294967294,300000);
    ERROR 1264 (22003): Out of range value for column 'id' at row 1
    mysql>
    
  • tinyint[(m)] [unsigned] [zerofill]

    有符号,取值范围:-128 ~ 127.
    无符号,取值范围:0 ~ 255
    
  • bigint[(m)][unsigned][zerofill]

    有符号,取值范围:-9223372036854775808 ~ 9223372036854775807
    无符号,取值范围:0  ~  18446744073709551615
    
  • decimal[(m[,d])] [unsigned] [zerofill]

    准确的小数值,m是数字总个数(负号不算),d是小数点后个数。 m最大值为65,d最大值为30。
    
    例如:
    create table L2(
    	id int not null primary key auto_increment,
    	salary decimal(8,2)
    )default charset=utf8;
    
    mysql> create table L2(id int not null primary key auto_increment,salary decimal(8,2))default charset=utf8;
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> insert into L2(salary) values(1.28);
    Query OK, 1 row affected (0.01 sec)
    
    mysql> insert into L2(salary) values(5.289);
    Query OK, 1 row affected, 1 warning (0.00 sec)
    
    mysql> insert into L2(salary) values(5.282);
    Query OK, 1 row affected, 1 warning (0.00 sec)
    
    mysql> insert into L2(salary) values(512132.28);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> insert into L2(salary) values(512132.283);
    Query OK, 1 row affected, 1 warning (0.00 sec)
    
    mysql> select * from L2;
    +----+-----------+
    | id | salary    |
    +----+-----------+
    |  1 |      1.28 |
    |  2 |      5.29 |
    |  3 |      5.28 |
    |  4 | 512132.28 |
    |  5 | 512132.28 |
    +----+-----------+
    5 rows in set (0.00 sec)
    
    mysql> insert into L2(salary) values(5121321.283);
    ERROR 1264 (22003): Out of range value for column 'salary' at row 1
    mysql>
    
  • FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]

    单精度浮点数,非准确小数值,m是数字总个数,d是小数点后个数。
    
  • DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]

    双精度浮点数(非准确小数值),m是数字总个数,d是小数点后个数。
    
  • char(m)

    定长字符串,m代表字符串的长度,最多可容纳255个字符。
    
    定长的体现:即使内容长度小于m,也会占用m长度。例如:char(5),数据是:yes,底层也会占用5个字符;如果超出m长度限制(默认MySQL是严格模式,所以会报错)。
        如果在配置文件中加入如下配置,
            sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
        保存并重启,此时MySQL则是非严格模式,此时超过长度则自动截断(不报错)。。
    
    注意:默认底层存储是固定的长度(不够则用空格补齐),但是查询数据时,会自动将空白去除。 如果想要保留空白,在sql-mode中加入 PAD_CHAR_TO_FULL_LENGTH 即可。
    查看模式sql-mode,执行命令:show variables  like 'sql_mode';
    
    一般适用于:固定长度的内容。
    
    create table L3(
        id int not null primary key auto_increment,
        name varchar(5),
        depart char(3)
    )default charset=utf8;
    
    insert into L3(name,depart) values("alexsb","sbalex");
    
  • varchar(m)

    变长字符串,m代表字符串的长度,最多可容纳65535个字节。
    
    变长的体现:内容小于m时,会按照真实数据长度存储;如果超出m长度限制((默认MySQL是严格模式,所以会报错)。
        如果在配置文件中加入如下配置,
            sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
        保存并重启,此时MySQL则是非严格模式,此时超过长度则自动截断(不报错)。
    
    例如:
    create table L3(
        id int not null primary key auto_increment,
        name varchar(5),
        depart char(3)
    )default charset=utf8;
    
    mysql> create table L3(id int not null primary key auto_increment,name varchar(5),depart char(3))default charset=utf8;
    Query OK, 0 rows affected (0.03 sec)
    
    -- 插入多行
    mysql> insert into L3(name,depart) values("wu","WU"),("wupei","ALS");
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    mysql> select * from L3;
    +----+-------+--------+
    | id | name  | depart |
    +----+-------+--------+
    |  1 | wu    | WU     |
    |  2 | wupei | ALS    |
    +----+-------+--------+
    2 rows in set (0.00 sec)
    
    -- 非严格模式下,不会报错。
    mysql> insert into L3(name,depart) values("wupeiqi","ALS");
    ERROR 1406 (22001): Data too long for column 'name' at row 1
    mysql> insert into L3(name,depart) values("wupei","ALSB");
    ERROR 1406 (22001): Data too long for column 'depart' at row 1
    mysql>
    
    -- 如果 sql-mode 中加入了 PAD_CHAR_TO_FULL_LENGTH ,则查询时char时空白会保留。
    mysql> select name,length(name),depart,length(depart) from L3;
    +-------+--------------+--------+----------------+
    | name  | length(name) | depart | length(depart) |
    +-------+--------------+--------+----------------+
    | wu    |            2 | WU     |              3 |
    | wupei |            5 | ALS    |              3 |
    +-------+--------------+--------+----------------+
    4 rows in set (0.00 sec)
    mysql>
    
  • text

    text数据类型用于保存变长的大字符串,可以组多到65535 (2**16 − 1)个字符。
    
    一般情况下,长文本会用text类型。例如:文章、新闻等。
    
    create table L4(
    	id int not null primary key auto_increment,
        title varchar(128),
    	content text
    )default charset=utf8;
    
  • mediumtext

    A TEXT column with a maximum length of 16,777,215 (2**24 − 1) characters.
    
  • longtext

    A TEXT column with a maximum length of 4,294,967,295 or 4GB (2**32 − 1)
    
  • datetime

    YYYY-MM-DD HH:MM:SS(1000-01-01 00:00:00/9999-12-31 23:59:59)
    
  • timestamp

    YYYY-MM-DD HH:MM:SS(1970-01-01 00:00:00/2037年)
    
    对于TIMESTAMP,它把客户端插入的时间从当前时区转化为UTC(世界标准时间)进行存储,查询时,将其又转化为客户端当前时区进行返回。
    
    对于DATETIME,不做任何改变,原样输入和输出。
    
    mysql> create table L5(
        -> id int not null primary key auto_increment,
        -> dt datetime,
        -> tt timestamp
        -> )default charset=utf8;
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> insert into L5(dt,tt) values("2025-11-11 11:11:44", "2025-11-11 11:11:44");
    
    mysql> select * from L5;
    +----+---------------------+---------------------+
    | id | dt                  | tt                  |
    +----+---------------------+---------------------+
    |  1 | 2025-11-11 11:11:44 | 2025-11-11 11:11:44 |
    +----+---------------------+---------------------+
    1 row in set (0.00 sec)
    
    mysql> show variables like '%time_zone%';
    +------------------+--------+
    | Variable_name    | Value  |
    +------------------+--------+
    | system_time_zone | CST    | 
    | time_zone        | SYSTEM |
    +------------------+--------+
    2 rows in set (0.00 sec)
    -- “CST”指的是MySQL所在主机的系统时间,是中国标准时间的缩写,China Standard Time UT+8:00
    
    mysql> set time_zone='+0:00';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> show variables like '%time_zone%';
    +------------------+--------+
    | Variable_name    | Value  |
    +------------------+--------+
    | system_time_zone | CST    |
    | time_zone        | +00:00 |
    +------------------+--------+
    2 rows in set (0.01 sec)
    
    mysql> select * from L5;
    +----+---------------------+---------------------+
    | id | dt                  | tt                  |
    +----+---------------------+---------------------+
    |  1 | 2025-11-11 11:11:44 | 2025-11-11 03:11:44 |
    +----+---------------------+---------------------+
    1 row in set (0.00 sec)
    
  • date

    YYYY-MM-DD(1000-01-01/9999-12-31)
    
  • time

    HH:MM:SS('-838:59:59'/'838:59:59')
    

MySQL还有很多其他的数据类型,例如:set、enum、TinyBlob、Blob、MediumBlob、LongBlob 等,详细见官方文档:https://dev.mysql.com/doc/refman/5.7/en/data-types.html

上述就是关于数据表的一些基本操作。

3.3 MySQL代码操作

基于Python去连接MySQL之后,想要进行数据表的管理的话,发送的指令其实都是相同的,例如:

import pymysql

# 连接MySQL
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8")
cursor = conn.cursor()

# 1. 创建数据库
"""
cursor.execute("create database db4 default charset utf8 collate utf8_general_ci")
conn.commit()
"""

# 2. 进入数据库、查看数据表
"""
cursor.execute("use db4")
cursor.execute("show tables")
result = cursor.fetchall()
print(result)
"""

# 3. 进入数据库创建表
cursor.execute("use db4")
sql = """
create table L4(
    id int not null primary key auto_increment,
    title varchar(128),
    content text,
    ctime datetime
)default charset=utf8;
"""
cursor.execute(sql)
conn.commit()

# 4. 查看数据库中的表
"""
cursor.execute("show tables")
result = cursor.fetchall()
print(result)
"""

# 5. 其他 drop table... 略过


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

4.数据行

当数据库和数据表创建完成之后,就需要对数据表中的内容进行:增、删、改、查了。

在这里插入图片描述

4.1 内置客户端操作

数据行操作的相关SQL语句(指令)如下:

  • 数据

    insert into 表名 (列名,列名,列名) values(对应列的值,对应列的值,对应列的值);
    
    insert into tb1(name,password) values('武沛齐','123123');
    insert into tb1(name,password) values('武沛齐','123123'),('alex','123');
    
    insert into tb1 values('武沛齐','123123'),('alex','123'); -- 如果表中只有2列
    
  • 除数据

    delete from 表名;
    delete from 表名 where 条件;
    
    delete from tb1;
    delete from tb1 where name="wupeiqi";
    delete from tb1 where name="wupeiqi" and password="123";
    delete from tb1 where id>9;
    
  • 数据

    update 表名 set 列名=;
    update 表名 set 列名=where 条件;
    
    update tb1 set name="wupeiqi";
    update tb1 set name="wupeiqi" where id=1;
    
    update tb1 set age=age+1;  -- 整型
    update tb1 set age=age+1 where id=2;
    
    update L3 set name=concat(name,"db");
    update L3 set name=concat(name,"123")  where id=2;  -- concat一个函数,可以拼接字符串
    
  • 询数据

    select * from 表名;
    select 列名,列名,列名 from 表名;
    select 列名,列名 as 别名,列名 from 表名;
    select * from 表名 where 条件;
    
    select * from tb1;
    select id,name,age from tb1;
    select id,name as N,age, from tb1;
    select id,name as N,age, 111 from tb1;
    
    select * from tb1 where id = 1;
    select * from tb1 where id > 1;
    select * from tb1 where id != 1;
    select * from tb1 where name="wupeiqi" and password="123";
    

4.2 Python代码操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R5JQnG2n-1647519355950)(assets/image-20210511102323966.png)]

import pymysql

# 连接MySQL,自动执行 use userdb; -- 进入数据库
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8", db='userdb')
cursor = conn.cursor()


# 1.新增(需commit)
"""
cursor.execute("insert into tb1(name,password) values('武沛齐','123123')")
conn.commit()
"""

# 2.删除(需commit)
"""
cursor.execute("delete from tb1 where id=1")
conn.commit()
"""

# 3.修改(需commit)
"""
cursor.execute("update tb1 set name='xx' where id=1")
conn.commit()
"""

# 4.查询
"""
cursor.execute("select * from tb where id>10")
data = cursor.fetchone() # cursor.fetchall()
print(data)
"""

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

其实在真正做项目开发时,流程如下:

  • 第一步:根据项目的功能来设计相应的 数据库 & 表结构(不会经常变动,在项目设计之初就确定好了)。
  • 第二步:操作表结构中的数据,已达到实现业务逻辑的目的。

例如:实现一个 用户管理系统。

先使用MySQL自带的客户端创建相关 数据库和表结构(相当于先创建好Excel结构)。

create database usersdb default charset utf8 collate utf8_general_ci;
create table users(
	id int not null primary key auto_increment,
    name varchar(32),
	password varchar(64)
)default charset=utf8;

再在程序中执行编写相应的功能实现 注册、登录 等功能。

import pymysql


def register():
    print("用户注册")

    user = input("请输入用户名:") # alex
    password = input("请输入密码:") # sb

    # 连接指定数据
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8", db="usersdb")
    cursor = conn.cursor()

    # 执行SQL语句(有SQL注入风险,稍后讲解)
    # sql = 'insert into users(name,password)values("alex","sb")'
    sql = 'insert into users(name,password) values("{}","{}")'.format(user, password)
    
    cursor.execute(sql)
    conn.commit()

    # 关闭数据库连接
    cursor.close()
    conn.close()

    print("注册成功,用户名:{},密码:{}".format(user, password))


def login():
    print("用户登录")

    user = input("请输入用户名:")
    password = input("请输入密码:")

    # 连接指定数据
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8", db="usersdb")
    cursor = conn.cursor()

    # 执行SQL语句(有SQL注入风险,稍后讲解)
    # sql = select * from users where name='wupeiqi' and password='123'
    sql = "select * from users where name='{}' and password='{}'".format(user, password)
    cursor.execute(sql)
    
    result = cursor.fetchone() # 去向mysql获取结果
    # None
    # (1,wupeiqi,123)
    
    
    # 关闭数据库连接
    cursor.close()
    conn.close()

    if result:
        print("登录成功", result)
    else:
        print("登录失败")


def run():
    choice = input("1.注册;2.登录")
    if choice == '1':
        register()
    elif choice == '2':
        login()
    else:
        print("输入错误")


if __name__ == '__main__':
    run()

So,你会发现, 在项目开发时,数据库 & 数据表 的操作其实就做那么一次,最最常写的还是 对数据行 的操作。

5.用户授权

1.新建用户

在MySQL的默认数据库 mysql 中的 user 表中存储着所有的账户信息(含账户、权限等)。

  • 创建和删除用户

    create user '用户名'@'连接者的IP地址' identified by '密码';
    
    create user wupeiqi1@127.0.0.1 identified by 'root123';
    drop user wupeiqi1@127.0.0.1;
    create user wupeiqi3@'%' identified by 'root123';
    drop user wupeiqi3@'%';   -- 用%替代用户IP,可实现任意IP登录
    
  • 修改用户

    rename user '用户名'@'IP地址' to '新用户名'@'IP地址';
    
    rename user 'wupeiqi1'@'127.0.0.1' to 'wupeiqi1'@'localhost';
    
  • 修改密码

    set password for '用户名'@'IP地址' = Password('新密码')
    
    set password for 'wupeiqi4'@'%' = Password('123123');
    

2.用户权限

创建好用户之后,就可以为用户进行授权了。

  • 授权

    grant 权限 on 数据库.表 to   '用户'@'IP地址'
    
    grant all privileges on *.* TO 'wupeiqi'@'localhost';         -- 用户wupeiqi拥有所有数据库的所有权限
    grant all privileges on day26.info TO 'wupeiqi'@'localhost';  -- 用户wupeiqi拥有数据库day26中info表的所有权限
    grant select on day26.info TO 'wupeiqi'@'localhost';          -- 用户wupeiqi拥有数据库day26中info表的查询权限
    grant select,insert on day26.* TO 'wupeiqi'@'localhost';      -- 用户wupeiqi拥有数据库day26所有表的查询和插入权限
    
    grant all privileges on day26db.* to 'wupeiqi4'@'%';
    
    
    注意:flush privileges;   -- 将数据读取到内存中,从而立即生效。
    
  • 查看授权

    show grants for '用户'@'IP地址'
    
    show grants for 'wupeiqi'@'localhost';
    show grants for 'wupeiqi4'@'%';
    
  • 取消授权

    revoke 权限 on 数据库.表 from '用户'@'IP地址'
    
    revoke ALL PRIVILEGES on day26.* from 'wupeiqi'@'localhost';
    
    revoke ALL PRIVILEGES on day26db.* from 'wupeiqi4'@'%';
    注意:flush privileges;   -- 将数据读取到内存中,从而立即生效。
    

6.关于SQL注入

假如,你开发了一个用户认证的系统,应该用户登录成功后才能正确的返回相应的用户结果。

import pymysql

# 输入用户名和密码
user = input("请输入用户名:") # ' or 1=1 -- 
pwd = input("请输入密码:") # 123


conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8",db='usersdb')
cursor = conn.cursor()

# 基于字符串格式化来 拼接SQL语句
# sql = "select * from users where name='alex' and password='123'"
# sql = "select * from users where name='' or 1=1 -- ' and password='123'"
sql = "select * from users where name='{}' and password='{}'".format(user, pwd)
cursor.execute(sql)

result = cursor.fetchone()
print(result) # None,不是None

cursor.close()
conn.close()

如果用户在输入user时,输入了: ' or 1=1 -- ,这样即使用户输入的密码不存在,也会可以通过验证。

为什么呢?

因为在SQL拼接时,拼接后的结果是:

select * from users where name='' or 1=1 -- ' and password='123'

注意:在MySQL中 -- 表示注释。

那么,在Python开发中 如何来避免SQL注入呢?

切记,SQL语句不要在使用python的字符串格式化,而是使用pymysql的execute方法。

import pymysql

# 输入用户名和密码
user = input("请输入用户名:")
pwd = input("请输入密码:")

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8", db='userdb')

cursor = conn.cursor()

cursor.execute("select * from users where name=%s and password=%s", [user, pwd])
# 或
# cursor.execute("select * from users where name=%(n1)s and password=%(n2)s", {"n1": user, 'n2': pwd})

result = cursor.fetchone()
print(result)

cursor.close()
conn.close()

Part2 必备SQL语句

1.条件

根据条件搜索结果。
在这里插入图片描述

select * from info where id between 2 and 4;   -- id大于等于2、且小于等于4
select * from info where (name = '李杰' or email="pyyu@live.com")  and age=49;
select * from info where id in (select id from depart);
# exists select * from depart where id=5,去查数据是否存在,如果存在,如果不存在。
select * from info where exists (select * from depart where id=5);
select * from info where not exists (select * from depart where id=5);
select * from (select * from info where id>2) as T where age > 10;

2.通配符

一般用于模糊搜索。(图表同上)

select * from info where email like "wupeiqi%";   #  % 可通配多个字符
select * from info where email like "__peiqi_live.co_"; # 一个_ 代指一个字符,

3.映射

在这里插入图片描述

select id, name as NM, 123  from info;
注意:少些select * ,自己需求。
select 
	id,
	name,
	( select title from depart where depart.id=info.depart_id) as x1,
	( select title from depart where depart.id=info.id) as x2
from info;
select 
	id,
	name,
	case depart_id when 1 then "第1部门" end v1,  # =1时 为第一部分,否则为null,表头v1
	case depart_id when 1 then "第1部门" else "其他" end v2,#=1时 为第一部分,否则为其他,表头v2
	case depart_id when 1 then "第1部门" when 2 then "第2部门" else "其他" end v3,
	case when age<18 then "少年" when age<30 then "青年" else "油腻男" end v6
from info;

4.排序

在这里插入图片描述

select * from info order by age desc; -- 倒序
select * from info order by age asc;  -- 顺序
select * from info order by age asc,id desc; --优先按照age从小到大;如果age相同则按照id从大到小
select * from info where id>10 order by age asc,id desc;
select * from info where id>6 or name like "%y" order by age asc,id desc;

5.取部分

一般要用于获取部分数据。

在这里插入图片描述

select * from info limit 5;   								-- 获取前5条数据
select * from info order by id desc limit 3;				-- 先排序,再获取前3条数据
select * from info where id > 4 order by id desc limit 3;	-- 先排序,再获取前3条数据
select * from info limit 3 offset 2;	-- 从位置2开始,向后获取前3数据

6.分组

select age,max(id),min(id),count(id),sum(id),avg(id) from info group by age;--count(*)计数
select depart_id,count(id) from info group by depart_id having count(id) > 2;
select age,name from info group by age;  -- 不建议
select * from info where id in (select max(id) from info group by age);
select age,count(id) from info group by age having count(id) > 2;
select age,count(id) from info where id > 4 group by age having count(id) > 2;  -- 聚合条件放在having后面
到目前为止SQL执行顺序:
    where 
    group by
    having 
    order by
    limit 
select age,count(id) from info where id > 2 group by age having count(id) > 1 order by age desc limit 1;
- 要查询的表info
- 条件 id>2
- 根据age分组
- 对分组后的数据再根据聚合条件过滤 count(id)>1
- 根据age从大到小排序
- 获取第1

7.左右连表

多个表可以连接起来进行查询。

在这里插入图片描述

展示用户信息&部门名称:

主表 left outer join 从表 on 主表.x = 从表.id 
select info.id,info.name,info.email,depart.title from info left outer join depart on info.depart_id = depart.id;
从表 right outer join 主表 on 主表.x = 从表.id
select info.id,info.name,info.email,depart.title from info right outer join depart on info.depart_id = depart.id;

为了更加直接的查看效果,我们分别在 depart 表 和 info 中额外插入一条数据。

insert into depart(title) values("运维");

这样一来主从表就有区别:

  • info主表,就以info数据为主,depart为辅。

    select info.id,info.name,info.email,depart.title from info left outer join depart on info.depart_id = depart.id;
    
  • depart主表,,就以depart数据为主,info为辅。

    select info.id,info.name,info.email,depart.title from info right outer join depart on info.depart_id = depart.id;
    
select * from info left outer join depart on ....
select * from depart left outer join info on ....

简写:select * from depart left join info on ....

-- 内连接:    表  inner join 表  on 条件
select * from info inner join depart on info.depart_id=depart.id;

+----+-----------+------------------+------+-----------+----+--------+
| id | name      | email            | age  | depart_id | id | title  |
+----+-----------+------------------+------+-----------+----+--------+
|  1 | 武沛齐    | wupeiqi@live.com |   19 |         1 |  1 | 开发   |
|  2 | 于超      | pyyu@live.com    |   49 |         1 |  1 | 开发   |
|  3 | alex      | alex@live.com    |    9 |         2 |  2 | 运营   |
|  4 | tony      | tony@live.com    |   29 |         1 |  1 | 开发   |
|  5 | kelly     | kelly@live.com   |   99 |         3 |  3 | 销售   |
|  6 | james     | james@live.com   |   49 |         1 |  1 | 开发   |
|  7 | 李杰      | lijie@live.com   |   49 |         1 |  1 | 开发   |
+----+-----------+------------------+------+-----------+----+--------+

8.联合

在这里插入图片描述

select id,title from depart 
union
select id,name from info;

select id,title from depart 
union
select email,name from info;-- 列数需相同
select id from depart 
union
select id from info;-- 自动去重
select id from depart 
union all
select id from info;-- 保留所有

9.外键约束

在上述的表:一对多的 info.depart_id字段、多对多的 boy_girl.boy_idgirl_id 直接用整型存储就可以,因为他们只要存储关联表的主键ID即可。

在开发中往往还会为他们添加一个 外键约束,保证某一个列的值必须是其他表中的特定列已存在的值,例如:info.depart_id的值必须是 depart.id中已存在的值。

一对多示例:
在这里插入图片描述

create table info(
	id int not null auto_increment primary key,
    name varchar(16) not null,
    email varchar(32) not null,
    age int,
    depart_id int not null,
    constraint fk_info_depart foreign key (depart_id) references depart(id) -- 外键
)default charset=utf8;

如果表结构已创建好了,额外想要增加外键:

alter table info add constraint fk_info_depart foreign key info(depart_id) references depart(id);

删除外键:

alter table info drop foreign key fk_info_depart;

多对多示例:
在这里插入图片描述

create table boy_girl(
	id int not null auto_increment primary key,
    boy_id int not null,
    girl_id int not null,
    constraint fk_boy_girl_boy foreign key boy_girl(boy_id) references boy(id),
    constraint fk_boy_girl_girl foreign key boy_girl(girl_id) references girl(id)
)default charset=utf8;

如果表结构已创建好了,额外想要增加外键:

alter table boy_girl add constraint fk_boy_girl_boy foreign key boy_girl(boy_id) references boy(id);
alter table boy_girl add constraint fk_boy_girl_girl foreign key boy_girl(girl_id) references girl(id);

删除外键:

alter table info drop foreign key fk_boy_girl_boy;
alter table info drop foreign key fk_boy_girl_girl;

Part3 索引

聚簇索引 和 非聚簇索引 底层均利用了B+Tree结构结构,只不过内部数据存储有些不同罢了。

在企业开发中一般都会使用 innodb 引擎(内部支持事务、行级锁、外键等特点),在MySQL5.5版本之后默认引擎也是innodb。

1.非聚簇索引(mysiam引擎)

create table 表名(
    id int not null auto_increment primary key, 
    name varchar(32) not null,
    age int
)engine=myisam default charset=utf8;

在这里插入图片描述

2.聚簇索引(innodb引擎)

create table 表名(
    id int not null auto_increment primary key, 
    name varchar(32) not null,
    age int
)engine=innodb default charset=utf8;

3.常见索引

在innodb引擎下,索引底层都是基于B+Tree数据结构存储(聚簇索引)。
在这里插入图片描述

在开发过程中常见的索引类型有:

  • 主键索引:加速查找、不能为空、不能重复。 + 联合主键索引
  • 唯一索引:加速查找、不能重复。 + 联合唯一索引
  • 普通索引:加速查找。 + 联合索引

3.1 主键和联合主键索引

create table 表名(
    id int not null auto_increment primary key,   -- 主键
    name varchar(32) not null
);

create table 表名(
    id int not null auto_increment,
    name varchar(32) not null,
    primary key(id)
);

create table 表名(
    id int not null auto_increment,
    name varchar(32) not null,
    primary key(1,2)          -- 如果有多列,称为联合主键(不常用且myisam引擎支持)
);
alter table 表名 add primary key(列名);
alter table 表名 drop primary key;

注意:删除索引时可能会报错,自增列必须定义为键。

ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key

alter table 表 change id id int not null;
create table t7(
    id int not null,
    name varchar(32) not null,
    primary key(id)
);

alter table t6 drop primary key;

3.2 唯一和联合唯一索引

create table 表名(
    id int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    unique ix_name (name),
    unique ix_email (email),
);

create table 表名(
    id int not null auto_increment,
    name varchar(32) not null,
    unique (1,2)               -- 如果有多列,称为联合唯一索引。
);
create unique index 索引名 on 表名(列名);
drop unique index 索引名 on 表名;

3.3 索引和联合索引

create table 表名(
    id int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    index ix_email (email),
    index ix_name (name),
);

create table 表名(
    id int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    index ix_email (name,email)     -- 如果有多列,称为联合索引。
);
create index 索引名 on 表名(列名);
drop index 索引名 on 表名;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值