MySQL

目录

一、Windows下载安装

二、数据库操作

三、数据库表基本操作

四、表内容操作

五、Navicat for MySQL

六、Python操作MySQL

七、SQLAlchemy

八、事务

一、Windows下载安装

1、下载

mysql-8.0.13-winx64
http://dev.mysql.com/downloads/mysql/

2、解压

如果想要让MySQL安装在指定目录,那么就将解压后的文件夹移动到指定目录,如:C:\mysql-8.0.13-winx64

添加环境变量:C:\mysql-8.0.13-winx64\bin

3、安装

  • 以管理员身份运行cmd,将路径切换到C:\mysql-8.0.13-winx64\bin,然后运行安装命令:mysqld -install;

  • 然后运行启动命令 net start mysql

  • 登录使用命令 mysql -u root -p1991

  • 会提示输入密码,默认 初始root的密码为空,故回车即可;

  • cmd进入mysql的bin目录,运行修改密码的命令 mysqladmin -u root -p password root,此时会提醒你输入旧的密码,因为默认是空,所以此时回车即可,至此mysql的root用户密码修改为root。

  • 在cmd里查看本机mysql的端口:按照前面的操作,登录mysql后,运行命令show global variables like 'port';切记每次命令最后要加上分号,否则无法生效;

4、Linux下操作

  • 安装
--yum -y install mariadb mariadb-server
OR
--yum -y install mysql mysql-server
  • 启动
--service mysqld start  #开启
--chkconfig mysqld on   #设置开机自启
OR
--systemctl start mariadb
--systemctl enable mariadb
  • 查看
-- ps aux |grep mysqld    #查看进程
-- netstat -an |grep 3306 #查看端口
  • 设置密码
-- mysqladmin -uroot password '123'   #设置初始密码,初始密码为空因此-p选项没有用
-- mysqladmin -u root -p123 password '1234' #修改root用户密码
  • 登陆
-- mysql               #本地登录,默认用户root,空密码,用户为root@127.0.0.1
-- mysql -uroot -p1234 #本地登录,指定用户名和密码,用户为root@127.0.0.1
-- mysql -uroot -p1234 -h 192.168.31.95 #远程登录,用户为root@192.168.31.95

5、常用命令

-- 启动mysql服务与停止mysql服务命令:
-- 
-- net start mysql
-- net stop  mysql
-- 
-- 
-- 登陆与退出命令:
-- 
--    mysql -h 服务器IP -P 端口号 -u  用户名 -p 密码 --prompt 命令提示符  --delimiter 指定分隔符
--    mysql -h 127.0.0.1 -P 3306 -uroot -p123
--    quit------exit----\q;
-- 
-- 
-- \s;   ------my.ini文件:[mysql] default-character-set=gbk [mysqld] character-set-server=gbk
-- 
-- prompt 命令提示符(\D:当前日期 \d:当前数据库  \u:当前用户)
-- 
-- \T(开始日志) \t(结束日志)
-- 
-- show warnings;
-- 
-- help() ? \h
-- 
-- \G;
-- 
-- select now(); 显示当前时间
-- select version();
-- select user;
-- 
-- \c 取消命令
-- 
-- delimiter 指定分隔符

6、sql及其规范

sql是Structured Query Language(结构化查询语言)的缩写。SQL是专为数据库而建立的操作命令集,是一种功能齐全的数据库语言。

<1> 在数据库系统中,SQL语句不区分大小写(建议用大写) 。但字符串常量区分大小写。建议命令大写,表名库名小写;

<2> SQL语句可单行或多行书写,以“;”结尾。关键词不能跨多行或简写。

<3> 用空格和缩进来提高语句的可读性。子句通常位于独立行,便于编辑,提高可读性。

<4> 注释:单行注释:--

               多行注释:/*......*/

<5>sql语句可以折行操作

<6> DDL,DML和DCL

-- -- DML(data manipulation language):
--    它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的
--    数据进行操作的语言
-- 
-- -- DDL(data definition language):
--    DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)
--    的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用
-- 
-- -- DCL(Data Control Language):
--    是数据库控制功能。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)
--    语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权
--    力执行DCL

二、数据库操作

1、显示数据库

show databases;
--默认数据库
    mysql - 用户权限相关数据
    sys - 用于系统测试数据
    information_schema - MySQL本身架构相关数据
show create database app01; --显示创建该数据库时的基本信息(比如编码方式)

2、创建数据库

-- utf-8
create database app09 default charset utf8 collate utf8_general_ci;
 
-- gbk
CREATE DATABASE 数据库名称 DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;(小写即可)

3、使用和删除数据库

USE db_name; -- 使用数据库
show tables; -- 显示当前数据库中的所有表
drop database 数据库名; -- 删除数据库
select database(); -- 检测当前所在的数据库

4、修改数据库

alter database db_name [character set xxx] 

5、用户管理

--创建用户
    create user '用户名'@'IP地址' identified by '密码';
--删除用户
    drop user '用户名'@'IP地址';
--修改用户
    rename user '用户名'@'IP地址'; to '新用户名'@'IP地址';;
--修改密码
    set password for '用户名'@'IP地址' = Password('新密码')
  
----用户权限相关数据保存在mysql数据库的user表中,所以也可以直接对其进行操作(不建议)

6、授权管理

show grants for '用户'@'IP地址'                  -- 查看权限
grant  权限 on 数据库.表 to   '用户'@'IP地址'      -- 授权
revoke 权限 on 数据库.表 from '用户'@'IP地址'      -- 取消权限
all privileges  除grant外的所有权限
            select          仅查权限
            select,insert   查和插入权限
            ...
            usage                   无访问权限
            alter                   使用alter table
            alter routine           使用alter procedure和drop procedure
            create                  使用create table
            create routine          使用create procedure
            create temporary tables 使用create temporary tables
            create user             使用create user、drop user、rename user和revoke  all privileges
            create view             使用create view
            delete                  使用delete
            drop                    使用drop table
            execute                 使用call和存储过程
            file                    使用select into outfile 和 load data infile
            grant option            使用grant 和 revoke
            index                   使用index
            insert                  使用insert
            lock tables             使用lock table
            process                 使用show full processlist
            select                  使用select
            show databases          使用show databases
            show view               使用show view
            update                  使用update
            reload                  使用flush
            shutdown                使用mysqladmin shutdown(关闭MySQL)
            super                   􏱂􏰈使用change master、kill、logs、purge、master和set global。还允许mysqladmin􏵗􏵘􏲊􏲋调试登陆
            replication client      服务器位置的访问
            replication slave       由复制从属使用
关于权限
对于目标数据库以及内部其他:
            数据库名.*           数据库中的所有
            数据库名.表          指定数据库中的某张表
            数据库名.存储过程     指定数据库中的存储过程
            *.*                所有数据库
关于数据库
 用户名@IP地址         用户只能在改IP下才能访问
用户名@192.168.1.%   用户只能在改IP段下才能访问(通配符%表示任意)
用户名@%             用户可以再任意IP下访问(默认IP地址为%)
关于用户和IP
 grant all privileges on db1.tb1 TO '用户名'@'IP'

grant select on db1.* TO '用户名'@'IP'

grant select,insert on *.* TO '用户名'@'IP'

revoke select on db1.tb1 from '用户名'@'IP'
示例

特殊的:

flush privileges,将数据读取到内存中,从而立即生效。

忘记密码怎么办?

-- 方法1:启动mysql时,跳过授权表
[root@controller ~]# service mysqld stop
[root@controller ~]# mysqld_safe --skip-grant-table &
[root@controller ~]# mysql
mysql> select user,host,password from mysql.user;
+----------+-----------------------+-------------------------------------------+
| user     | host                  | password                                  |
+----------+-----------------------+-------------------------------------------+
| root     | localhost             | *A4B6157319038724E3560894F7F932C8886EBFCF |
| root     | localhost.localdomain |                                           |
| root     | 127.0.0.1             |                                           |
| root     | ::1                   |                                           |
|          | localhost             |                                           |
|          | localhost.localdomain |                                           |
| root     | %                     | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 |
+----------+-----------------------+-------------------------------------------+
mysql> update mysql.user set password=password("123") where user="root" and host="localhost";
mysql> flush privileges;
mysql> exit
[root@controller ~]# service mysqld restart
[root@controller ~]# mysql -uroot -p123


--方法2(删库):
--删除与权限相关的库mysql,所有的授权信息都丢失,主要用于测试数据库或者刚刚建库不久没有授权数据的情况(从删库到跑路)
[root@controller ~]# rm -rf /var/lib/mysql/mysql
[root@controller ~]# service mysqld restart
[root@controller ~]# mysql

三、数据库表基本操作

1、创建表

create table 表名(
    列名  类型  是否可以为空,
    列名  类型  是否可以为空
)ENGINE=InnoDB DEFAULT CHARSET=utf8

gender  tinyint(1)  # 性别可以用这种方式表示,0或非0,如果你用boolean表示,系统会自动转换成tinyint
是否可空,null表示空,非字符串
            not null    - 不可空
            null        - 可空
是否可以为空
默认值,创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值
            create table tb1(
                nid int not null defalut 2,
                num int not null
            )
默认值
自增,如果为某列设置自增列,插入数据时无需设置此列,默认将自增(表中只能有一个自增列)
            create table tb1(
                nid int not null auto_increment primary key,
                num int null
            )
            或
            create table tb1(
                nid int not null auto_increment,
                num int null,
                index(nid)
            )
            注意:1、对于自增列,必须是索引(含主键)。
                 2、对于自增可以设置步长和起始值
                     show session variables like 'auto_inc%';
                     set session auto_increment_increment=2;
                     set session auto_increment_offset=10;

                     shwo global  variables like 'auto_inc%';
                     set global auto_increment_increment=2;
                     set global auto_increment_offset=10;
自增
主键,一种特殊的唯一索引,不允许有空值,如果主键使用单个列,则它的值必须唯一,如果是多列,则其组合必须唯一。
            create table tb1(
                nid int not null auto_increment primary key,
                num int null
            )
            或
            create table tb1(
                nid int not null,
                num int not null,
                primary key(nid,num)
            )
主键
外键,一个特殊的索引,只能是指定内容
            creat table color(
                nid int not null primary key,
                name char(16) not null
            )

            create table fruit(
                nid int not null primary key,
                smt char(32) null ,
                color_id int not null,
                constraint fk_cc foreign key (color_id) references color(nid)
            )
外键

2、删除表

drop table 表名

3、清空表

delete from 表名  -- 清空表格,逐条删除,返回影响的条数;
truncate table 表名  -- 整体清空,再重建表格,字段跟原来一样;信息比较多时,比较快。

4、修改表

--添加列:alter table tab_name add [column] 列名 类型[完整性约束条件][first|after 字段名];
    --添加多个字段
      alter table users2 
            add addr varchar(20),
            add age  int first,
            add birth varchar(20) after name;
--删除列:alter table 表名 drop column 列名
--修改列-类型:
        alter table tab_name modify 列名 类型 [完整性约束条件][first|after 字段名];

--修改列名:
        alter table tab_name change [column] 列名 新列名 类型 [完整性约束条件][first|after 字段名];

--修改表名:rename table 表名 to 新表名;

--修该表所用的字符集:alter table student character set utf8;
  
--添加主键:
        alter table 表名 add primary key(列名);
--删除主键:
        alter table 表名 drop primary key;
        alter table 表名  modify  列名 int, drop primary key;
  
--添加外键:alter table 从表 add constraint 外键名称(形如:FK_从表_主表) foreign key 从表(外键字段) references 主表(主键字段);
--删除外键:alter table 表名 drop foreign key 外键名称
  
--修改默认值:ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000;
--删除默认值:ALTER TABLE testalter_tbl ALTER i DROP DEFAULT;

5、查看表

desc 表名;

6、索引

索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。创建与维护索引会消耗很多时间与磁盘空间,但查询速度大大提高!

  • 索引语法
--创建表时
--语法:
    CREATE TABLE 表名 (
                字段名1  数据类型 [完整性约束条件…],
                字段名2  数据类型 [完整性约束条件…],
                [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                [索引名]  (字段名[(长度)]  [ASC |DESC])
                );

---------------------------------

--创建普通索引示例:

    CREATE TABLE emp1 (
        id INT,
        name VARCHAR(30) ,
        resume VARCHAR(50),
        INDEX index_emp_name (name)
    --KEY index_dept_name (dept_name)
        );

--创建唯一索引示例:

    CREATE TABLE emp2 (
        id INT,
        name VARCHAR(30) ,
        bank_num CHAR(18) UNIQUE ,
        resume VARCHAR(50),
        UNIQUE INDEX index_emp_name (name)
        );

--创建全文索引示例:

    CREATE TABLE emp3 (
        id INT,
        name VARCHAR(30) ,
        resume VARCHAR(50),
        FULLTEXT INDEX index_resume (resume)
        );

--创建多列索引(联合索引)示例:

    CREATE TABLE emp4 (
        id INT,
        name VARCHAR(30) ,
        resume VARCHAR(50),
        INDEX index_name_resume (name, resume)
        );

-----------------------------------

--添加索引

    --CREATE在已存在的表上创建索引
      CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名
              ON 表名 (字段名[(长度)]  [ASC |DESC]) ;
    
    --ALTER TABLE在已存在的表上创建索引
    
      ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                    索引名 (字段名[(长度)]  [ASC |DESC]) ;
    
    
     CREATE INDEX index_emp_name on emp1(name);
     ALTER TABLE emp2 ADD UNIQUE INDEX index_bank_num(band_num);

-- 删除索引
    
    --语法:DROP INDEX 索引名 on 表名
    DROP INDEX index_emp_name on emp1;
    DROP INDEX bank_num on emp2;

7、外键约束

  • 创建外键
----  每一个班主任会对应多个学生 , 而每个学生只能对应一个班主任

-----主表

CREATE TABLE ClassCharger(

       id TINYINT PRIMARY KEY auto_increment,
       name VARCHAR (20),
       age INT ,
       is_marriged boolean  -- show create table ClassCharger: tinyint(1)

);

INSERT INTO ClassCharger (name,age,is_marriged) VALUES ("冰冰",12,0),
                                                       ("丹丹",14,0),
                                                       ("歪歪",22,0),
                                                       ("姗姗",20,0),
                                                       ("小雨",21,0);


----子表

CREATE TABLE Student(

       id INT PRIMARY KEY auto_increment,
       name VARCHAR (20),
       charger_id TINYINT,     --切记:作为外键一定要和关联主键的数据类型保持一致
       -- [ADD CONSTRAINT charger_fk_stu] FOREIGN KEY (charger_id) REFERENCES ClassCharger(id);

) ENGINE=INNODB;

INSERT INTO Student(name,charger_id) VALUES ("alvin1",2),
                                            ("alvin2",4),
                                            ("alvin3",1),
                                            ("alvin4",3),
                                            ("alvin5",1),
                                            ("alvin6",3),
                                            ("alvin7",2);


DELETE FROM ClassCharger WHERE name="冰冰";
INSERT student (name,charger_id) VALUES ("yuan",1);
-- 删除居然成功,可是 alvin3显示还是有班主任id=1的冰冰的;

-----------增加外键和删除外键---------
--添加外键:alter table 从表 add constraint 外键名称(形如:FK_从表_主表) foreign key 从表(外键字段) references 主表(主键字段)
ALTER TABLE student  ADD CONSTRAINT abc
                     FOREIGN KEY(charger_id)
                     REFERENCES  classcharger(id);


ALTER TABLE student DROP FOREIGN KEY abc;
  • INNODB支持的ON语句
-----------------innodb支持的四种方式---------------------------------------

-----cascade方式 在父表上update/delete记录时,同步update/delete掉子表的匹配记录
-----外键的级联删除:如果父表中的记录被删除,则子表中对应的记录自动被删除--------

     FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)
                              ON DELETE CASCADE


------set null方式 在父表上update/delete记录时,将子表上匹配记录的列设为null
   -- 要注意子表的外键列不能为not null

     FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)
                              ON DELETE SET NULL


------Restrict方式 :拒绝对父表进行删除更新操作(了解)

------No action方式 在mysql中同Restrict,如果子表中有匹配的记录,则不允许对父表对应候选键
-- 进行update/delete操作(了解)
  • ENGINE=InnoDB
1、MySQL默认采用的是MyISAM。

2、MyISAM不支持事务,而InnoDB支持。InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事务,自动提交,这样会影响速度,所以最好是把多条SQL语句显示放在begin和commit之间,组成一个事务去提交。

3、InnoDB支持数据行锁定,MyISAM不支持行锁定,只支持锁定整个表。即MyISAM同一个表上的读锁和写锁是互斥的,MyISAM并发读写时如果等待队列中既有读请求又有写请求,默认写请求的优先级高,即使读请求先到,所以MyISAM不适合于有大量查询和修改并存的情况,那样查询进程会长时间阻塞。因为MyISAM是锁表,所以某项读操作比较耗时会使其他写进程饿死。
4、InnoDB支持外键,MyISAM不支持。
5、InnoDB的主键范围更大,最大是MyISAM的2倍。
6、InnoDB不支持全文索引,而MyISAM支持。全文索引是指对char、varchar和text中的每个词(停用词除外)建立倒排序索引。MyISAM的全文索引其实没啥用,因为它不支持中文分词,必须由使用者分词后加入空格再写到数据表里,而且少于4个汉字的词会和停用词一样被忽略掉。

7、MyISAM支持GIS数据,InnoDB不支持。即MyISAM支持以下空间数据对象:Point,Line,Polygon,Surface等。

8、没有where的count(*)使用MyISAM要比InnoDB快得多。因为MyISAM内置了一个计数器,count(*)时它直接从计数器中读,而InnoDB必须扫描全表。所以在InnoDB上执行count(*)时一般要伴随where,且where中要包含主键以外的索引列。为什么这里特别强调“主键以外”?因为InnoDB中primary index是和raw data存放在一起的,而secondary index则是单独存放,然后有个指针指向primary key。所以只是count(*)的话使用secondary index扫描更快,而primary key则主要在扫描索引同时要返回raw data时的作用较大。
MyISAM和InnoDB

8、基本数据类型

MySQL的数据类型大致分为:数值、时间和字符串

  • 数值型

  • 时间型

  •  字符串型

 

总结:

1、一个汉字占多少长度与编码有关:

UTF-8:一个汉字=3个字节

GBK:一个汉字=2个字节

2、varchar(n) 表示 n 个字符,无论汉字和英文,Mysql 都能存入 n 个字符,仅是实际字节长度有所区别

3、MySQL 检查长度,可用 SQL 语言来查看:

select 列名 from 表名

4、int(m) 里的 m 是表示 SELECT 查询结果集中的显示宽度,并不影响实际的取值范围,没有影响到显示的宽度,不知道这个 m 有什么用。

5、设一个字段定义为 float(5,3),如果插入一个数 123.45678,实际数据库里存的是 123.457,但总个数还以实际为准,即 6 位。

6、若定义一个字段为timestamp,这个字段里的时间数据会随其他字段修改的时候自动刷新,所以这个数据类型的字段可以存放这条记录最后被修改的时间。

char 和 varchar:

  •  1.char(n) 若存入字符数小于n,则以空格补于其后,查询之时再将空格去掉。所以 char 类型存储的字符串末尾不能有空格,varchar 不限于此。

  •  2.char(n) 固定长度,char(4) 不管是存入几个字符,都将占用 4 个字节,varchar 是存入的实际字符数 +1 个字节(n<=255)或2个字节(n>255),所以 varchar(4),存入 3 个字符将占用 4 个字节。

  •  3.char 类型的字符串检索速度要比 varchar 类型的快。

varchar 和 text:

  •  1.varchar 可指定 n,text 不能指定,内部存储 varchar 是存入的实际字符数 +1 个字节(n<=255)或 2 个字节(n>255),text 是实际字符数 +2 个字节。

  •  2.text 类型不能有默认值。

  •  3.varchar 可直接创建索引,text 创建索引要指定前多少个字符。varchar 查询速度快于 text, 在都创建索引的情况下,text 的索引似乎不起作用。

更多参考:

  • http://www.runoob.com/mysql/mysql-data-types.html
  • http://dev.mysql.com/doc/refman/5.7/en/data-type-overview.html

四、表内容操作

1、增

insert into 表 (列名,列名...) values (值,值,值...)
insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...)
insert into 表 (列名,列名...) select (列名,列名...) frominsert intovalues (值,值,值...)  # 直接添加,必须一一对应,一个字段不能少

create table employee_new(
                 id int primary key auto_increment,
                 name varchar(20) not null unique,
                 birthday varchar(20),
                 salary float(7,2)
                             );

       insert into employee_new (id,name,birthday,salary) values
                     (1,'yuan','1990-09-09',9000);

       insert into employee_new values
       (2,'alex','1989-08-08',3000);

       insert into employee_new (name,salary) values
       ('xialv',1000);

      -- 插入多条数据
       insert into employee_new values
       (4,'alvin1','1993-04-20',3000),
       (5,'alvin2','1995-05-12',5000);

      -- set插入: insert [into] tab_name set 字段名=值

      insert into employee_new set id=12,name="alvin3";

2、删

delete fromdelete fromwhere id=1 and name='alex'
--delete只能用来删除一行记录
--delete语句只能删除表中的内容,不能删除表本身,想要删除表,用drop
delete from employee;  
--逐条删除表记录,比较慢;注意auto_increment没有被重置:alter table employee auto_increment=1;
--TRUNCATE TABLE也可以删除表中的所有数据,词语句首先摧毁表,再新建表。此种方式删除的数据不能在事务中恢复,适合数据量很大时;

3、改

update 表 set name = 'alex' where id>1

4、查

select * from 表
select * from 表 where id > 1
select nid,name,gender as gg from 表 where id > 1  # as后面的名字只是临时显示,不会改变字段名,也可以不写
select distinct JS ,name from ExamResult; # 过滤表中重复数据

5、单表查询

1、条件
    select * fromwhere id > 1 and name != 'alex' and num = 12;
 
    select * fromwhere id between 5 and 16;
 
    select * fromwhere id in (11,22,33)
    select * fromwhere id not in (11,22,33)
    select * fromwhere id in (select nid from 表)
    select name fromwhere age is null; # 查询字段age为空的人名
 
2、通配符
    select * fromwhere name like 'ale%'  - ale开头的所有(多个字符串)
    select * fromwhere name like 'ale_'  - ale开头的所有(一个字符)
 
3、限制
    select * from 表 limit 5;            - 前5行
    select * from 表 limit 4,5;          - 从第4行开始的5行
    select * from 表 limit 5 offset 4    - 从第4行开始的5行
 
4、排序
    select * fromorder byasc              - 根据 “列” 从小到大排列
    select * fromorder bydesc             - 根据 “列” 从大到小排列
    select * fromorder by 列1 desc,列2 asc    - 根据 “列1” 从大到小排列,如果相同则按列2从小到大排序
 
5、分组
    select num fromgroup by num
    select num,nid fromgroup by num,nid
    select num,nid fromwhere nid > 10 group by num,nid order nid desc
    select num,nid,count(*),sum(score),max(score),min(score) fromgroup by num,nid
 
    select num fromgroup by num having max(id) > 10
 
    特别的:group by 必须在where之后,order by之前
    having 和 where两者都可以对查询结果进行进一步的过滤,差别有:
                     <1>where语句只能用在分组之前的筛选,having可以用在分组之后的筛选;
                     <2>使用where语句的地方都可以用having进行替换
                     <3>having中可以用聚合函数,where中就不行。    
  
 
6、聚合函数: 先不要管聚合函数要干嘛,先把要求的内容查出来再包上聚合函数即可。
                 --   (一般和分组查询配合使用)

        --<1> 统计表中所有记录

            -- COUNT(列名):统计行的个数
                    -- 统计一个班级共有多少学生?先查出所有的学生,再用count包上
                     select count(*) from ExamResult;
                    -- 统计JS成绩大于70的学生有多少个?
                     select count(JS) from ExamResult where JS>70;
                    -- 统计总分大于280的人数有多少?
                     select count(name) from ExamResult
                           where (ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0))>280;
                    -- 注意:count(*)统计所有行;     count(字段)不统计null值.

            -- SUM(列名):统计满足条件的行的内容和
                    -- 统计一个班级JS总成绩?先查出所有的JS成绩,再用sum包上
                        select JS as JS总成绩 from ExamResult;
                        select sum(JS) as JS总成绩 from ExamResult;
                    -- 统计一个班级各科分别的总成绩
                        select sum(JS) as JS总成绩,
                               sum(Django) as Django总成绩,
                               sum(OpenStack) as OpenStack from ExamResult;

                    -- 统计一个班级各科的成绩总和
                        select sum(ifnull(JS,0)+ifnull(Django,0)+ifnull(Database,0))
                                                    as 总成绩 from ExamResult;
                    -- 统计一个班级JS成绩平均分
                        select sum(JS)/count(*) from ExamResult ;
                    -- 注意:sum仅对数值起作用,否则会报错。

            -- AVG(列名):
                            -- 求一个班级JS平均分?先查出所有的JS分,然后用avg包上。
                                select avg(ifnull(JS,0)) from ExamResult;
                            -- 求一个班级总分平均分
                                select avg((ifnull(JS,0)+ifnull(Django,0)+ifnull(Database,0)))
                                                         from ExamResult ;
            -- Max、Min
                            -- 求班级最高分和最低分(数值范围在统计中特别有用)
                              select Max((ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0)))
                              最高分 from ExamResult;
                              select Min((ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0)))
                              最低分 from ExamResult;
                            -- 求购物表中单价最高的商品名称及价格
                              ---SELECT id, MAX(price) FROM order_menu;--id和最高价商品是一个商品吗?
                              
                              SELECT MAX(price) FROM order_menu; 

                            -- 注意:null 和所有的数计算都是null,所以需要用ifnull将null转换为0!
                            --      -----ifnull(JS,0)

7、重点:Select from where group by having order by
                -- Mysql在执行sql语句时的执行顺序:
                -- from  where  select group by  having order by limit 
                -- 分析:
                select JS as JS成绩 from ExamResult where JS成绩 >70; ---- 不成功
                select JS as JS成绩 from ExamResult having JS成绩 >90; --- 成功
8、使用正则表达式查询
        SELECT * FROM employee WHERE emp_name REGEXP '^yu';

        SELECT * FROM employee WHERE emp_name REGEXP 'yun$';

        SELECT * FROM employee WHERE emp_name REGEXP 'm{2}';

6、多表查询

准备表

-- 准备两张表
-- company.employee
-- company.department

      create table employee(
      emp_id int auto_increment primary key not null,
      emp_name varchar(50),
      age int,
      dept_id int
      );

      insert into employee(emp_name,age,dept_id) values
        ('A',19,200),
        ('B',26,201),
        ('C',30,201),
        ('D',24,202),
        ('E',20,200),
        ('F',38,204);


    create table department(
       dept_id int,
       dept_name varchar(100)
      );

    insert into department values
      (200,'人事部'),
      (201,'技术部'),
      (202,'销售部'),
      (203,'财政部');

mysql> select * from employee;
+--------+----------+------+---------+
| emp_id | emp_name | age  | dept_id |
+--------+----------+------+---------+
|      1 | A        |   19 |     200 |
|      2 | B        |   26 |     201 |
|      3 | C        |   30 |     201 |
|      4 | D        |   24 |     202 |
|      5 | E        |   20 |     200 |
|      6 | F        |   38 |     204 |
+--------+----------+------+---------+
6 rows in set (0.00 sec)

mysql> select * from department;
+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
|     200 | 人事部    |
|     201 | 技术部    |
|     202 | 销售部    |
|     203 | 财政部    |
+---------+-----------+
4 rows in set (0.01 sec)

多表查询之连接查询

1.笛卡尔积查询

mysql> SELECT * FROM employee,department;

--        select employee.emp_id,employee.emp_name,employee.age,
--        department.dept_name from employee,department;

+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age  | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
|      1 | A        |   19 |     200 |     200 | 人事部    |
|      1 | A        |   19 |     200 |     201 | 技术部    |
|      1 | A        |   19 |     200 |     202 | 销售部    |
|      1 | A        |   19 |     200 |     203 | 财政部    |
|      2 | B        |   26 |     201 |     200 | 人事部    |
|      2 | B        |   26 |     201 |     201 | 技术部    |
|      2 | B        |   26 |     201 |     202 | 销售部    |
|      2 | B        |   26 |     201 |     203 | 财政部    |
|      3 | C        |   30 |     201 |     200 | 人事部    |
|      3 | C        |   30 |     201 |     201 | 技术部    |
|      3 | C        |   30 |     201 |     202 | 销售部    |
|      3 | C        |   30 |     201 |     203 | 财政部    |
|      4 | D        |   24 |     202 |     200 | 人事部    |
|      4 | D        |   24 |     202 |     201 | 技术部    |
|      4 | D        |   24 |     202 |     202 | 销售部    |
|      4 | D        |   24 |     202 |     203 | 财政部    |
|      5 | E        |   20 |     200 |     200 | 人事部    |
|      5 | E        |   20 |     200 |     201 | 技术部    |
|      5 | E        |   20 |     200 |     202 | 销售部    |
|      5 | E        |   20 |     200 |     203 | 财政部    |
|      6 | F        |   38 |     204 |     200 | 人事部    |
|      6 | F        |   38 |     204 |     201 | 技术部    |
|      6 | F        |   38 |     204 |     202 | 销售部    |
|      6 | F        |   38 |     204 |     203 | 财政部    |
+--------+----------+------+---------+---------+-----------+

2.内连接

-- 查询两张表中都有的关联数据,相当于利用条件从笛卡尔积结果中筛选出了正确的结果。

  select * from employee,department where employee.dept_id = department.dept_id;
--select * from employee inner join department on employee.dept_id = department.dept_id;

      +--------+----------+------+---------+---------+-----------+
      | emp_id | emp_name | age  | dept_id | dept_id | dept_name |
      +--------+----------+------+---------+---------+-----------+
      |      1 | A        |   19 |     200 |     200 | 人事部    |
      |      2 | B        |   26 |     201 |     201 | 技术部    |
      |      3 | C        |   30 |     201 |     201 | 技术部    |
      |      4 | D        |   24 |     202 |     202 | 销售部    |
      |      5 | E        |   20 |     200 |     200 | 人事部    |
      +--------+----------+------+---------+---------+-----------+

select e.emp_id as id,e.emp_name as name,d.dept_name as depart_name from employee e,department d where e.dept_id = d.dept_id;
+----+------+-------------+
| id | name | depart_name |
+----+------+-------------+
|  1 | A    | 人事部      |
|  2 | B    | 技术部      |
|  3 | C    | 技术部      |
|  4 | D    | 销售部      |
|  5 | E    | 人事部      |
+----+------+-------------+

3.外连接

--(1)左外连接:在内连接的基础上增加左边有右边没有的结果

 select * from employee left join department on employee.dept_id = department.dept_id;

     +--------+----------+------+---------+---------+-----------+
    | emp_id | emp_name | age  | dept_id | dept_id | dept_name |
    +--------+----------+------+---------+---------+-----------+
    |      1 | A        |   19 |     200 |     200 | 人事部    |
    |      5 | E        |   20 |     200 |     200 | 人事部    |
    |      2 | B        |   26 |     201 |     201 | 技术部    |
    |      3 | C        |   30 |     201 |     201 | 技术部    |
    |      4 | D        |   24 |     202 |     202 | 销售部    |
    |      6 | F        |   38 |     204 |    NULL | NULL      |
    +--------+----------+------+---------+---------+-----------+

 --(2)右外连接:在内连接的基础上增加右边有左边没有的结果

 select * from employee RIGHT JOIN department on employee.dept_id = department.dept_id;

        +--------+----------+------+---------+---------+-----------+
        | emp_id | emp_name | age  | dept_id | dept_id | dept_name |
        +--------+----------+------+---------+---------+-----------+
        |      1 | A        |   19 |     200 |     200 | 人事部    |
        |      2 | B        |   26 |     201 |     201 | 技术部    |
        |      3 | C        |   30 |     201 |     201 | 技术部    |
        |      4 | D        |   24 |     202 |     202 | 销售部    |
        |      5 | E        |   20 |     200 |     200 | 人事部    |
        |   NULL | NULL     | NULL |    NULL |     203 | 财政部    |
        +--------+----------+------+---------+---------+-----------+

 --(3)全外连接:在内连接的基础上增加左边有右边没有的和右边有左边没有的结果

    -- mysql不支持全外连接 full JOIN
    -- mysql可以使用此种方式间接实现全外连接
    
   select * from employee RIGHT JOIN department on employee.dept_id = department.dept_id
   UNION
   select * from employee LEFT JOIN department on employee.dept_id = department.dept_id;

        

        +--------+----------+------+---------+---------+-----------+
        | emp_id | emp_name | age  | dept_id | dept_id | dept_name |
        +--------+----------+------+---------+---------+-----------+
        |      1 | A        |   19 |     200 |     200 | 人事部    |
        |      2 | B        |   26 |     201 |     201 | 技术部    |
        |      3 | C        |   30 |     201 |     201 | 技术部    |
        |      4 | D        |   24 |     202 |     202 | 销售部    |
        |      5 | E        |   20 |     200 |     200 | 人事部    |
        |   NULL | NULL     | NULL |    NULL |     203 | 财政部    |
        |      6 | F        |   38 |     204 |    NULL | NULL      |
        +--------+----------+------+---------+---------+-----------+

      -- 注意 union与union all的区别:union会去掉相同的纪录

多表查询之复合条件连接查询

-- 查询员工年龄大于等于25岁的人所在的部门

    SELECT DISTINCT department.dept_name
    FROM employee,department
    WHERE employee.dept_id = department.dept_id
    AND age>25;


--以内连接的方式查询employee和department表,并且以age字段的升序方式显示

    select employee.emp_id,employee.emp_name,employee.age,department.dept_name
    from employee,department
    where employee.dept_id = department.dept_id
    order by age asc;

多表查询之子查询

-- 子查询是将一个查询语句嵌套在另一个查询语句中。
-- 内层查询语句的查询结果,可以为外层查询语句提供查询条件。
-- 子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字
-- 还可以包含比较运算符:= 、 !=、> 、<等


-- 1. 带IN关键字的子查询

   ---查询employee表,但dept_id必须在department表中出现过

   select * from employee
            where dept_id IN
            (select dept_id from department);


+--------+----------+------+---------+
| emp_id | emp_name | age  | dept_id |
+--------+----------+------+---------+
|      1 | A        |   19 |     200 |
|      2 | B        |   26 |     201 |
|      3 | C        |   30 |     201 |
|      4 | D        |   24 |     202 |
|      5 | E        |   20 |     200 |
+--------+----------+------+---------+
5 rows in set (0.01 sec)



-- 2. 带比较运算符的子查询
      --      =、!=、>、>=、<、<=、<>

     -- 查询员工年龄大于等于25岁的部门
     select dept_id,dept_name from department
           where dept_id IN
          (select DISTINCT dept_id from employee where age>=25);

-- 3. 带EXISTS关键字的子查询

-- EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录。
-- 而是返回一个真假值。Ture或False
-- 当返回Ture时,外层查询语句将进行查询;当返回值为False时,外层查询语句不进行查询

     select * from employee
              WHERE EXISTS
              (SELECT dept_name from department where dept_id=203);

      --department表中存在dept_id=203,Ture


     select * from employee
                WHERE EXISTS
              (SELECT dept_name from department where dept_id=205);

     -- Empty set (0.00 sec)


    --通过子查询复制一张表的内容到一个新创建的表中:  create table t1(select * from t2);

五、Navicat for MySQL

1、下载安装:Navicat for MySQL_11.2.15 

2、破解:下载破解文件PatchNavicat.exe,然后选择刚刚安装的Navicat安装路径下找到navicat.exe文件,点击选择即可激活成功。

Navicat for MySQL 安装软件和破解补丁:

链接:https://pan.baidu.com/s/1oKcErok_Ijm0CY9UjNMrnA   密码:4xb1

3、打开Navicat for MySQL,设置连接

  • 在终端打开MySQL,输入ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'mysql的密码';回车    别漏了后面有个分号    mysql的密码是安装mysql时设置的密码;

  • 然后点击连接,输入连接名和你的密码,点击测试连接,成功之后确定;

六、Python操作MySQL

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

  • 原生模块 pymsql
  • ORM框架 SQLAchemy

2、执行SQL

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()
View Code

3、获取新创建数据自增ID

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

4、获取查询数据

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()
print(row_3)  
conn.commit()
cursor.close()
conn.close()
View Code

注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:

  • cursor.scroll(1,mode='relative')  # 相对当前位置移动
  • cursor.scroll(2,mode='absolute') # 相对绝对位置移动

5、fetch数据类型

  • 关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:
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()
View Code

七、SQLAlchemy

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

2、安装:pip3 install SQLAlchemy

3、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

4、内部处理

  • 使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
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

5、ORM功能使用

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

  (1)创建表

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)
    
 
 
class Server(Base):
    __tablename__ = 'server'
 
    id = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(64), unique=True, nullable=False)
    port = Column(Integer, default=22)
 
 
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)操作表

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker,relationship
from sqlalchemy import create_engine

engine = create_engine("mysql+pymysql://root:1991@127.0.0.1:3306/charlie", max_overflow=5)
Base = declarative_base()

# 创建单表
class Users(Base):
    __tablename__ = 'users'
    # primary_key设置主键,autoincrement设置自增
    id = Column(Integer, primary_key=True,autoincrement=True)
    name = Column(String(32))
    extra = Column(String(16))
    num = Column(Integer)
    #定制输出格式,默认输出的是对象
    def __repr__(self):
        str = '%s %s %s %s'%(self.id,self.name,self.extra,self.num)
        return str


# 一对多
class Favor(Base):
    __tablename__ = 'favor'
    nid = Column(Integer, primary_key=True,autoincrement=True)
    caption = Column(String(50))
    def __repr__(self):
        str = '%s'%(self.caption)
        return str


class Person(Base):
    __tablename__ = 'person'
    nid = Column(Integer, primary_key=True,autoincrement=True)
    name = Column(String(32), index=True, nullable=True)
    favor_id = Column(Integer, ForeignKey("favor.nid"))
    #与创建对象无关,方便查询,relationship尽量和外键放一起
    favor = relationship("Favor", backref='ttt')
    def __repr__(self):
        str = '%s %s %s'%(self.name,self.favor_id,self.favor)
        return str


# 多对多

class HostToHostUser(Base):
    '''关系对象'''
    __tablename__ = 'host_to_host_user'
    nid = Column(Integer, primary_key=True, autoincrement=True)
    host_user_id = Column(Integer, ForeignKey('host_user.id'))
    host_id = Column(Integer, ForeignKey('host.id'))
    #新方式一,添加relationship
    # host = relationship('Host',backref='h')
    # host_user = relationship('HostUser',backref='u')

class Host(Base):
    '''主机对象'''
    __tablename__ = 'host'
    id = Column(Integer, primary_key=True)
    hostname = Column(String(64), unique=True, nullable=False)
    #新方式二,这种方式,关系对象要放最上边
    host_user = relationship('HostUser',secondary=HostToHostUser.__tablename__,backref='hu')


class HostUser(Base):
    '''用户对象'''
    __tablename__ = 'host_user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(64), unique=True, nullable=False)


#创建所有表
# Base.metadata.create_all(engine)
#删除以上创建的所有表
# Base.metadata.drop_all(engine)

#操作表必须创建一个Session
Session = sessionmaker(bind=engine)
session = Session()


#========一对多操作============
#增加一条
obj = Users(name='alex',extra='dd',num=1)
session.add(obj)
#批量添加
session.add_all([
    Favor(caption='red'),
    Favor(caption='white'),
    Favor(caption='yy'),
])
session.add_all([
    Person(name="ff",favor_id=15),
    Person(name="hh",favor_id=16),
])
session.commit()

#
session.query(Users).filter(Users.id < 4).delete()
session.commit()
session.query(Users).delete()
session.commit()

#
session.query(Person).filter(Person.name=='xxs').update({"favor_id": 12})
#synchronize_session=False,字符串相加的标志
session.query(Users).filter(Users.id > 6).update({Users.name: "new_" + Users.name},
                                                 synchronize_session=False)
#synchronize_session="evaluate",数字相加必须有的标志
session.query(Users).filter(Users.id > 2).update({"num": Users.num + 1},
                                                 synchronize_session="evaluate")
session.commit()

# 查询===>filter_by(name='new')
#得到的一个Users对象的列表
ret = session.query(Users).all()
for i in ret:
    print(i)
#元组的形式输出name和extra
ret = session.query(Users.name, Users.extra).all()
print(ret)
#得到一个users对象
ret = session.query(Users).filter_by(name='new_captain').all()
print(ret[0])
# #得到第一个
ret = session.query(Users).filter_by(name='new_captain').first()
print(ret)

# 查询===>filter(Users.name == 'new')
ret = session.query(Users).filter(Users.name == 'new_captain').all()
print(ret[0])

#查看代码生成的SQL语句
ret = session.query(Users)
print(ret)

#多表查询join()
ret = session.query(Person).join(Favor).all()
for item in ret:
    print(item.name,item.favor_id)
ret = session.query(Person,Favor).join(Favor).all()
for item in ret:
    print(item)
#只输出每个人对应的爱好
ret = session.query(Person.name,Favor.caption).join(Favor).all()
print(ret)


'''favor = relationship("Favor", backref='ttt'),与创建对象无关,方便查询,
其实就是内部做了连表操作,内部默认给Favor表添加了一个新的叫‘ttt’的列,
虚拟的,只能调用,看不到,每一个"ttt"包含Person里面所有对应的行,这句关联代码
要跟外键代码放在一起,在一个类里'''

#新方式,正向查询
ret = session.query(Person).all()
for item in ret:
    print(item.name,item.favor_id,item.favor.caption)
#pis 12 black
#新方式,反向查询
ret = session.query(Favor).all()
for item in ret:
    print('%s-%s:%s'%(item.nid,item.caption,item.ttt))
#12-black:[pis 12 black]


#========多对多操作============

#批量添加两个表格内容
session.add_all([
    Host(hostname='h1'),
    Host(hostname='h2'),
    Host(hostname='h3'),
])
session.add_all([
    HostUser(username="a"),
    HostUser(username="b"),
    HostUser(username="c"),
])
session.commit()
#添加关系表格内容
session.add_all([
    HostToHostUser(host_id=1,host_user_id=1),
    HostToHostUser(host_id=1,host_user_id=2),
    HostToHostUser(host_id=2,host_user_id=1),
    HostToHostUser(host_id=2,host_user_id=2),
    HostToHostUser(host_id=3,host_user_id=1),
])
session.commit()

#普通方法查询host中h1对应的所有用户

#首先获取h1对象,all()获取对象列表,所以用first,只获取第一个
h1_obj = session.query(Host).filter(Host.hostname=='h1').first()
# print(h1_obj.id)
#获取关系表中group_id = g1_obj.id 的所有host_user_id
h1_host_user_id = session.query(HostToHostUser.host_user_id).filter(HostToHostUser.host_id==h1_obj.id).all()
print(h1_host_user_id)#[(1,), (2,)]
#处理集合
r = zip(*h1_host_user_id)
# print(list(r)[0])#(1, 2)得到第一个
#最后,根据用户ID获取所有用户
users = session.query(HostUser.username).filter(HostUser.id.in_(list(r)[0])).all()
print(users)#[('a',), ('b',)]
for item in users:
    print(item[0])#a,b


#新方式一查找,relationship放在关系对象中,只能先反向,后正向找
host_obj = session.query(Host).filter_by(hostname='h1').first()
for item in host_obj.h:
    print(item.host_user.username)

#新方式二查找,relationship放在其中一个表中,方法更简单,关系对象要放在最上边,可以双向查找
#正向查找
host_obj = session.query(Host).filter_by(hostname='h2').first()
for item in host_obj.host_user:
    print(item.username)#h1,h2
#反向查找
host_obj = session.query(HostUser).filter_by(username='b').first()
for item in host_obj.hu:
    print(item.hostname)#h1,h2
View Code

 

# 条件
ret = session.query(Users).filter_by(name='alex').all()
ret = session.query(Users).filter(Users.id > 1, Users.name == 'eric').all()
ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all()
ret = session.query(Users).filter(Users.id.in_([1,3,4])).all()
ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all()
ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='eric'))).all()
from sqlalchemy import and_, or_
ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all()
ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all()
ret = session.query(Users).filter(
    or_(
        Users.id < 2,
        and_(Users.name == 'eric', Users.id > 3),
        Users.extra != ""
    )).all()


# 通配符
ret = session.query(Users).filter(Users.name.like('e%')).all()
ret = session.query(Users).filter(~Users.name.like('e%')).all()

# 限制
ret = session.query(Users)[1:2]

# 排序
ret = session.query(Users).order_by(Users.name.desc()).all()
ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all()

# 分组
from sqlalchemy.sql import func

ret = session.query(Users).group_by(Users.extra).all()
ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name).all()

ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all()

# 连表

ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all()

ret = session.query(Person).join(Favor).all()

ret = session.query(Person).join(Favor, isouter=True).all()


# 组合
q1 = session.query(Users.name).filter(Users.id > 2)
q2 = session.query(Favor.caption).filter(Favor.nid < 2)
ret = q1.union(q2).all()

q1 = session.query(Users.name).filter(Users.id > 2)
q2 = session.query(Favor.caption).filter(Favor.nid < 2)
ret = q1.union_all(q2).all()
View Code

 八、事务

1、事务命令

-- start transaction 开启事务
-- Rollback 回滚事务,即撤销指定的sql语句(只能回退insert delete update语句),回滚到上一次commit的位置
-- Commit 提交事务,提交未存储的事务

-- savepoint 保留点 ,事务处理中设置的临时占位符 你可以对它发布回退(与整个事务回退不同)  

2、保留点savepoint

create table test2(id int PRIMARY KEY auto_increment,name VARCHAR(20)) engine=innodb;
INSERT INTO test2(name) VALUE ("alvin"),
                              ("yuan"),
                              ("xialv");

start transaction;
insert into test2 (name)values('silv');
select * from test2;
commit;

-- 保留点

start transaction;
insert into test2 (name)values('wu');
savepoint insert_wu;
select * from test2;

delete from test2 where id=4;
savepoint delete1;
select * from test2;

delete from test2 where id=1;
savepoint delete2;
select * from test2;

rollback to delete1;

select * from test2;

3、python中调用数据库启动事务的方式

import pymysql

#添加数据

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='yyy')

cursor = conn.cursor()

try:
    insertSQL0="INSERT INTO ACCOUNT2 (name,balance) VALUES ('oldboy',4000)"
    insertSQL1="UPDATE account2 set balance=balance-30 WHERE name='yuan'"
    insertSQL2="UPDATE account2 set balance=balance+30 WHERE name='xialv'"

    cursor = conn.cursor()

    cursor.execute(insertSQL0)
    conn.commit()

    cursor.execute(insertSQL1)
    raise Exception
    cursor.execute(insertSQL2)
    cursor.close()
    conn.commit()

except Exception as e:
    conn.rollback()
    conn.commit()

cursor.close()
conn.close()

4、事务特性

<1> 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

<2> 一致性(Consistency):事务前后数据的完整性必须保持一致。在事务执行之前数据库是符合数据完整性约束的,无论事务是否执行成功,事务结束后的数据库中的数据也应该是符合完整性约束的。在某一时间点,如果数据库中的所有记录都能保证满足当前数据库中的所有约束,则可以说当前的数据库是符合数据完整性约束的。 比如删部门表前应该删掉关联员工(已经建立外键),如果数据库服务器发生错误,有一个员工没删掉,那么此时员工的部门表已经删除,那么就不符合完整性约束了,所以这样的数据库也就性能太差啦!

<3>隔离性(Isolation):事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

<4>持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

5、隔离性:

  将数据库设计为串行化程的数据库,让一张表在同一时间内只能有一个线程来操作。如果将数据库设计为这样,那数据库的效率太低了。所以数据库的设计这没有直接将数据库设计为串行化,而是为数据库提供多个隔离级别选项,使数据库的使用者可以根据使用情况自己定义到底需要什么样的隔离级别。

不考虑隔离性可能出现的问题:

  • 脏读
-- 一个事务读取到了另一个事务未提交的数据,这是特别危险的,要尽力防止。
        a 1000
        b 1000
        a:
            start transactionupdate set money=money+100 where name=b;
        b:
            start transaction;
            select * from account where name=b;--1100
            commit;
        a:
            rollback;
        b:  start transaction;
            select * from account where name=b;--1000
  • 不可重复读
-- 在一个事务内读取表中的某一行数据,多次读取结果不同。(一个事务读取到了另一个事务已经提交
-- 的数据--增加记录、删除记录、修改记录),在某写情况下并不是问题,在另一些情况下就是问题。

a:
start transaction;
select 活期账户 from account where name=b;--1000    活期账户:1000
select 定期账户 from account where name=b;--1000   定期账户:1000
select 固定资产 from account where name=b;--1000   固定资产:1000
------------------------------
b:
start transaction;
update set money=0 where name=b;
commit;
------------------------------
select 活期+定期+固定 from account where name=b; --2000 总资产: 2000
  • 虚读
-- 是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。(一个事务读取到了另一个事务已经提交的数据---增加记录、删除记录)
-- 在某写情况下并不是问题,在另一些情况下就是问题。

b 1000
c 2000
d 3000
a:
start transaction
select sum(money) from account;---3000       3000
-------------------
d:start transaction;
insert into account values(d,3000);
commit;
-------------------
select count(*)from account;---3                         3
3000/3 = 1000                                            1000 

四个隔离级别:

  Serializable--可避免脏读、不可重复读、虚读情况的发生。(串行化)
  Repeatable read--可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
  Read committed--可避免脏读情况发生(读已提交)
  Read uncommitted--最低级别,以上情况均无法保证。(读未提交)
  • 安全性考虑:Serializable>Repeatable read>Read committed>Read uncommitted

  • 数据库效率:Read uncommitted>Read committed>Repeatable read>Serializable

一般情况下,我们会使用Repeatable read、Read committed mysql数据库默认的数据库隔离级别Repeatable read

mysql中设置数据库的隔离级别语句:

set [global/session] transaction isolation level xxxx;

  如果使用global则修改的是数据库的默认隔离级别,所有新开的窗口的隔离级别继承自这个默认隔离级别如果使用session修改,则修改的是当前客户端的隔离级别,和数据库默认隔离级别无关。当前的客户端是什么隔离级别,就能防止什么隔离级别问题,和其他客户端是什么隔离级别无关。

 

posted @ 2018-12-10 11:02  Charlie大夫  阅读(326)  评论(0编辑  收藏  举报