多表查询,初识pymysql模块

一、 多表连接查询

外链接语法
select 字段列表
    from  表1 (inner\left\right) join 表2
    on 表1.字段 = 表2.字段;

 

 1 #建表
 2 #部门表
 3 create table department(
 4 id int,
 5 name varchar(20) 
 6 );
 7 
 8 create table employee(
 9 id int primary key auto_increment,
10 name varchar(20),
11 sex enum('male','female') not null default 'male',
12 age int,
13 dep_id int
14 );
15 
16 #给两个表插入一些数据
17 insert into department values
18 (200,'技术'),
19 (201,'人力资源'),
20 (202,'销售'),
21 (203,'运营'); #注意这一条数据,在下面的员工表里面没有对应这个部门的数据
22 
23 insert into employee(name,sex,age,dep_id) values
24 ('egon','male',18,200),
25 ('alex','female',48,201),
26 ('wupeiqi','male',38,201),
27 ('yuanhao','female',28,202),
28 ('liwenzhou','male',18,200),
29 ('jingliyang','female',18,204) #注意这条数据的dep_id字段的值,这个204,在上面的部门表里面也没有对应的部门id。所以两者都含有一条双方没有涉及到的数据,这都是为了演示一下效果设计的昂
30 ;
练习资料

 交叉查询(生成笛卡儿积):

mysql> select * from department,employee; #表用逗号分隔,看我查询时表的顺序,先department后employee,所以你看结果表的这些字段,
是不是就是我们两个表字段并且哪个表在前面,哪个表的字段就在前面

 

注意: sql中的字符串, 用单引号,否则在linux的mysql库中执行,可能会报错

 

 

 

 

1.内连接(只连接匹配的行):

#我们要找的数据就是员工表里面dep_id字段的值和部门表里面id字段的值能对应上的那些数据啊,所以你看下面的写法:
mysql> select * from employee,department where employee.dep_id=department.id;
但是你看,我们左表employee表中的dep_id为204的那个数据没有了,右表department表的id为203的数据没有了,
因为我们现在要的就是两表能对应上的数据一起查出来,那个204和203双方对应不上。

 基于上面内容:我要查出技术部的员工的名字

mysql> select employee.name from employee,department where employee.dep_id=department.id and department.name='技术';

外链接之左链接(优先显示左表全部记录)

#以左表为准,即找出所有员工信息,当然包括没有部门的员工
#本质就是:在内连接的基础上增加左边有右边没有的结果  #注意语法:
select employee.id,employee.name,department.name as depart_name from employee left join department on employee.dep_id = department.id;

外链接之右链接(优先显示右表全部记录)

#以右表为准,即找出所有部门信息,包括没有员工的部门
#本质就是:在内连接的基础上增加右边有左边没有的结果
select employee.id,employee.name,department.name as depart_name from employee right join department on employee.dep_id=department.id;

全外链接(显示左右两个表记录)

全外连接:在内连接的基础上增加左边有右边没有的和右边有左边没有的结果
#注意:mysql不支持全外连接 full JOIN
#强调:mysql可以使用此种方式间接实现全外连接
select * from employee left join department on employee.dep_id = department.id
union
select * from employee right join department on employee.dep_id = department.id;

#注意 union与union all的区别:union会去掉相同的纪录,因为union all是left join 和right join合并,所以有重复的记录,通过union就将重复的记录去重了。

二、符合条件连表查询

#示例1:以内连接的方式查询employee和department表,并且employee表中的age字段值必须大于25,即找出年龄大于25岁的员工以及员工所在的部门
select employee.name,department.name from employee inner join department
    on employee.dep_id = department.id
    where age > 25;  #inner join 意思是从中间合并,就是保留你和我都有的数据. on 后面是共有内容的检索依据

 

#示例2:以内连接的方式查询employee和department表,并且以age字段的升序方式显示
select employee.id,employee.name,employee.age,department.name from employee,department
    where employee.dep_id = department.id
    and age > 25
    order by age asc;  # 排序顺序设置为age的升序(小的在前面);des是降序

三、子查询

介绍:
子查询其实就是将你的一个查询结果用括号括起来,这个结果也是一张表,就可以将它交给另外一个sql语句,作为它的一个查询依据来进行操作。

子查询的写法:
#首先从部门表里面找到技术部门对应的id
select id from department where name='技术';
#那我们把上面的查询结果用括号括起来,它就表示一条id=200的数据,然后我们通过员工表来查询dep_id=这条数据作为条件来查询员工的name
select name from employee where dep_id = (select id from department where name='技术');

补充
子查询:
#1:子查询是将一个查询语句嵌套在另一个查询语句中。
#2:内层查询语句的查询结果,可以为外层查询语句提供查询条件。
#3:子查询中可以包含:in、not in 、any、all、exists 和 not exists等关键字
#4:还可以包含比较运算符:= 、 !=、> 、<等

   1.子查询中带in关键字的子查询

#查询员工平均年龄在25岁以上的部门名,可以用连表,也可以用子查询,我们用子查询来搞一下
select id,name from department
    where id in 
        (select dep_id from employee group by dep_id having avg(age) > 25);
#连表来搞一下上面这个需求
select department.name from department inner join employee on department.id=employee.dep_id 
    group by department.name 
    having avg(age)>25;
#查看不足1人的部门名(子查询得到的是有人的部门id)
select name from department where id not in (select distinct dep_id from employee);

   2.子查询中带比较运算符的子查询

#比较运算符:=、!=、>、>=、<、<=、<>(不等于)
#查询大于所有人平均年龄的员工名与年龄
select name,age from emp where age > (select avg(age) from emp);
#查询大于部门内平均年龄的员工名、年龄
select t1.name,t1.age from emp t1
inner join 
(select dep_id,avg(age) avg_age from emp group by dep_id) t2
on t1.dep_id = t2.dep_id
where t1.age > t2.avg_age; 

   3.子查询中带exists()关键字的字查询

在使用exists()关键字时,内层查询语句不返回查询的记录。而是返回True或False.
当返回True时,外层查询语句将进行查询;当返回值为False时,外层查询语句不进行查询。
#department表中存在dept_id=203,Ture
select * from employee
where exists     #exists() 相当于一个函数,返回值是false\True
(select id from department where id=200);

二、IDE工具(Navicat)

Navicat这个工具本质上就是一个socket客户端,并且他是图形界面版的。我们使用它和直接使用命令行的区别就类似linux和windows系统操作起来的一个区别。
       下载链接:https://pan.baidu.com/s/1bpo5mqj
  Navicat的安装教程看这篇博客:https://www.cnblogs.com/clschao/articles/10022040.html
注意:
批量加注释:ctrl+?
键批量去注释:ctrl+shift+?

三、pymysql模块

pymysql本质上就是一个套接字客户端,只不过这个套接字客户端是在python程序中用的,它和mysql自带的那个客户端是一样的
import pymysql
#建立连接,得到一个通道 conn
= pymysql.connect( host='127.0.0.1',#localhost port=3306, user='root', password='222', #字符串 database='ku0', charset='utf8' ) cursor = conn.cursor(pymysql.cursors.DictCursor) #获取列表括起来的字典类型数据的游标;如果括号为空,则返回的是个元组 #设置指令 sql = 'show databases;' res = cursor.execute(sql) #res受影响的行 print(cursor.fetchall()) print(res)

 

cursor=conn.cursor()
sql='select * from userinfo where name="%s" and password="%s"' %(user,pwd) #注意%s需要加引号,执行这句sql的前提是已有个userinfo表,里面有name和password两个字段
print(sql) #打印出 用户输入数据后sql语句的样子
res=cursor.execute(sql)#返回sql查询成功的记录数目,是个数字,是受sql语句影响到的记录行数

   关于游标操作

#这个取数据的操作就像读取文件内容一样,每次read之后,光标就移动到了对应的位置.
可以移动游标的位置,继续取我们前面的数据,通过cursor.scroll(数字,模式),第一个参数就是一个int类型的数字,表示往后移动的记录条数,第二个参数为移动的模式,有两个值:absolute:绝对移动,relative:相对移动
#绝对移动:它是相对于所有数据的起始位置开始往后面移动的
#相对移动:他是相对于游标的当前位置开始往后移动的

#绝对移动的演示
#print(cursor.fetchall())
#cursor.scroll(3,'absolute') #从初始位置往后移动三条,那么下次取出的数据为第四条数据
#print(cursor.fetchone())

#相对移动的演示
#print(cursor.fetchone())
#cursor.scroll(1,'relative') #通过上面取了一次数据,游标的位置在第二条的开头,我现在相对移动了1个记录,那么下次再取,取出的是第三条,我相对于上一条,往下移动了一条
#print(cursor.fetchone())

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

 四、execute() #sql语句注入,执行(有过滤机制)

execut()  在python中的作用是执行sql语句,并返回受到影响的行数
在提交的语句中,有滤特殊数据的功能,例如 注释(-- ),or ... ,存在则提交失败,返回False\0.

 

有过滤特殊内容的作用
sql='insert into userinfo(name,password) values(%s,%s);'
res=cursor.execute(sql,("root","123456")) #执行sql语句,返回sql影响成功的行数

还可以进行更改操作:
res=cursor.excute("update userinfo set username='taibaisb' where id=2")

正常执行
rea = cursor.execute(sql) #执行sql语句,返回sql影响成功的行数

还可以一次执行多个数据
res=cursor.executemany(sql,[("root","123456"),("lhf","12356"),("eee","156")]) #执行sql语句,返回sql影响成功的行数,一次插多条记录

代码讲解特殊情况:

import pymysql
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='666',
    database='crm',
    charset='utf8'
)
cursor = conn.cursor(pymysql.cursors.DictCursor) #设置通道的输出格式. [{},{},...]
uname = input('请输入用户名:')
pword = input('请输入密码:')
sql = "select * from userinfo where username='%s' and password='%s';"%(uname,pword)
res = cursor.execute(sql) #res我们说是得到的行数,如果这个行数不为零,说明用户输入的用户名和密码存在,如果为0说名存在,你想想对不
print(res) #如果输入的用户名和密码错误,这个结果为0,如果正确,这个结果为1
if res:
    print('登陆成功')
else:
    print('用户名和密码错误!')
#然后我们再来看一个例子,直接连用户名和密码都不知道,但是依然能够登陆成功的情况:
请输入用户名:xxx' or 1=1 -- xxxxxx
请输入密码:
select * from userinfo where username='xxx' or 1=1 -- xxxxxx' and password='';
我们只输入了一个xxx' 加or 加 1=1 加 -- 加任意字符串
看上面被执行的sql语句你就发现了,or 后面跟了一个永远为真的条件,那么即便是username对不上,但是or后面的条件是成立的,也能够登陆成功

 解决方案

在服务端来解决sql注入的问题:不要自己来进行sql字符串的拼接了,pymysql能帮我们拼接,他能够防止sql注入,所以以后我们再写sql语句的时候按下面的方式写:
uname = input('请输入用户名:') 
pword = input('请输入密码:')
sql = "select * from userinfo where username=%s and password=%s;"
print(sql)
res = cursor.execute(sql,[uname,pword]) #res我们说是得到的行数,如果这个行数不为零,说明用户输入的用户名和密码存在,如果为0说名不存在
print(res) #如果输入的用户名和密码错误,这个结果为0,如果正确,这个结果为1
if res:
    print('登陆成功')
else:
    print('用户名和密码错误!')

 五、pycharm里增,删,改数据库(最后要conn.commit())

commit()   
#所有execute() 执行的数据,最后向数据库提交commit(),才算真的提交

 

import pymysql
conn=pymysql.connect(host='localhost',
    port=3306,#端口号一点要是数字类型
    user='root',
    password='222',
    database='ku0',
    charset='utf8')
cursor=conn.cursor()
sql='insert into tu values(5,"小慧4",99111);' #向tu表中插入数据
        # sql='show databases;'  #查看数据库里面的表
        # sql='select * from tu;'  #查看表中的内容
rea = cursor.execute(sql) #执行sql语句,返回sql影响成功的行数
conn.commit()  #向数据库提交,相当于 ;
print(cursor.fetchall())
print(rea) #打印受影响的行数
cursor.close()  
conn.close()

 六、pycharm里查找表中数据:fetchone,fetchmany,fetchall

cursor.fetchone()  #一次取一个数据
cursor.fetchmany(n) #可以指定取多少条数据
cursor.fetchall()  #取出剩下所有的

 示例:

import pymysql
conn=pymysql.connect(host='localhost',user='root',password='222',database='ku0')
cursor=conn.cursor()
sql='select * from tu;'
rows=cursor.execute(sql) #执行sql语句,返回sql影响成功的行数rows,将结果放入一个集合,等待被查询
res1=cursor.fetchone()
res4=cursor.fetchmany(2)
res5=cursor.fetchall()
print(res1)
print(res4)
print(res5)
print('%s rows in set (0.00 sec)' %rows)

 七、lastrowid(查看插入的最后一条数据的自增ID)

import pymysql
conn=pymysql.connect(host='localhost',user='root',password='222',database='ku0')
cursor=conn.cursor()
sql='insert into tu values(0,"小黑2",1888);'  #因为id设置为了主键,不能为空,所以传个0
rows=cursor.execute(sql)
print(cursor.lastrowid) #在插入语句后查看
conn.commit()
cursor.close()
conn.close()

 

posted @ 2019-01-18 17:45  Tank-Li  阅读(461)  评论(0编辑  收藏  举报