【数据库进阶】
关于连表
1
2
3
|
左右连表:
join
上下连表:
union
#自动去重 (当两张表里的数据,有重复的才会自动去重)
union
all
#不去重
|
1
2
3
4
5
6
7
8
|
#上下连表示例:
select
sid,sname
from
sname
union
select
tid,tname
from
teacher
select
sid,sname
from
student
UNION
ALL
select
sid,sname
from
student
|
视图 (不常用,开发过程中不长用,在开发语句中写,不要在数据库中写)
视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,并可以将其当作表来使用。
在一张物理表中,通过某种条件查询到的信息,重新生成一个新的虚拟表,这个表是通过创建时的语句,从物理表中实时拿取数据。
视图不能进行增 改 操作,视图对应的原物理表数据变更的话,视图表内的数据也会改变!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#语法
创建视图:
语法:
create
view
视图名
AS
sql语句
例子:
create
view
v1
as
select
*
from
teacher
where
tid >2;
删除视图:
drop
view
视图名
更改视图:
语法:
alter
view
视图名
AS
sql语句
更改视图其实更改的就是对应的sql语句
使用视图:
使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。
select
*
from
v1;
|
触发器 (查询不会引发此操作!)
当对某表里的某行,做增删改操作时,可在完成之前或是之后去触发执行某个操作;
当对某表里的某行,做增删改操作时,可以使用触发器自定义关联行为。
当有相关的操作时候,每执行一次相关操作,触发器也会执行一次。触发器操作,分为执行前触发和执行之后触发!
1
|
CREATE
TRIGGER
tri_after_update_tb1
AFTER
UPDATE
ON
tb1
FOR
EACH ROW
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
#语法:
增
#在执行增加操作前:
create
trigger
操作名字(自定义,不重复) before
insert
on
要操作的表名
for
each row
begin
函数体,执行sql操作的语句
end
#在执行增加操作后:
create
trigger
操作名字(自定义,不重复)
after
insert
on
要操作的表名
for
each row
begin
函数体,执行sql操作的语句
end
删
#在执行删除操作前:
create
trigger
操作名字(自定义,不重复) before
drop
on
要操作的表名
for
each row
begin
函数体,执行sql操作的语句
end
#在执行删除操作后:
create
trigger
操作名字(自定义,不重复)
after
drop
on
要操作的表名
for
each row
begin
函数体,执行sql操作的语句
end
改
#在执行更改操作前:
create
trigger
操作名字(自定义,不重复) before
update
on
要操作的表名
for
each row
begin
函数体,执行sql操作的语句
end
#在执行更改操作后:
create
trigger
操作名字(自定义,不重复)
after
update
on
要操作的表名
for
each row
begin
函数体,执行sql操作的语句
end
|
在定义触发器之前,可以先把sql语句终止时用分号触发执行的操作改成别的符号,然后再更改回来;当函数体中有多条更改语句的时候,每条语句之后需要用分号结尾。若想不与系统冲突,用 delimiter更改sql操作终止的标点符号,修改完之后,等操作结束再改回原分号“;” 不能影响别的操作行为。例如:delimiter //
触发器举例:
1
2
3
4
5
6
7
8
9
10
|
delimiter //
create
trigger
t1 BEFORE
INSERT
on
student
for
EACH ROW
BEGIN
INSERT
into
teacher(tname)
values
(NEW.sname);
INSERT
into
teacher(tname)
values
(NEW.sname);
INSERT
into
teacher(tname)
values
(NEW.sname);
INSERT
into
teacher(tname)
values
(NEW.sname);
END
//
delimiter ;
insert
into
student(gender,class_id,sname)
values
(
'男'
,1,
'陈XX'
),(
'女'
,1,
'李XX'
);
|
NEW 代表触发器外执行对应操作,新插入代码中的数据! 常用于 insert into 插入新数据,new 获取要插入的内容
OLD 代表触发器外执行对应操作,对应的原代码中要变更或是要删除的数据! 常用于 删和改 drop和update
删除触发器: drop trigger 触发器名
函数
函数执行方法:select 函数名
内置函数
1
2
3
|
时间格式化函数 date_format()
语法:date_format(具体时间,格式化操作时间结构,不同的占位符)
例如: date_format(ctime,
"%Y-%m"
) 显示年月
|
自定义函数(存在于数据库中,设置好后,在python中直接调用即可)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
语法:
数据类型 是强制型的,创建时指定的什么类型,就只能传入什么类型
create
function
函数名(
参数1 类型1
参数2 类型2
...
)
returns
数据类型 #指定函数返回值的数据类型
begin
declare
(声明变量) 变量名 数据类型
default
初始值
函数体,执行sql操作的语句
end
示例:
delimiter \\
create
function
f1(
i1
int
,
i2
int
)
returns
int
BEGIN
declare
num
int
;
set
num = i1 + i2;
return
(num);
END
\\
delimiter ;
|
自定义函数时需注意:
函数必须有返回值;
函数内部不能写 select * from tb1 这种sql语句;
删除函数: drop function 函数名
存储过程
存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。
存储过程 可以写SQL语句!!!
是保存在MySQL上的一个别名,他代指着很多的SQL语句操作,只要使用这个别名就能查到结果!用于替代程序员写SQL语句。为了职责更明确,可以把SQL语句放在程序里边,有以下几种操作方式:
1
2
3
4
5
6
7
8
9
|
方式一:
MySQL: 存储过程
程序:调用存储过程
方式二:
MySQL:。。(不操作)
程序:SQL语句
方式三:
MySQL:。。(不操作)
程序:类和对象(SQL语句)(ORM框架)
|
创建存储过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
语法:
简单类型:
delimiter //
create
procedure
名字()
begin
SQL语句
end
//
delimiter ;
传参数:(
in
,
out
,inout)<参数不管是否使用,都必须传值,而且存储过程是没有返回值的!!!>
delimiter //
create
procedure
p2(
in
n1
int
,
in
n2
int
)
BEGIN
sql语句
END
//
delimiter ;
|
执行存储过程:
1
2
3
4
|
SQL:call 名字()
py:
cursor
.callproc(
"名字"
,(传参))
cursor
.
commit
() 一定要提交操作
cursor
.fetchall() 获取查询到的结果集
|
删除存储过程:
1
|
drop
procedure
名字;
|
分类举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
1. 简单
create
procedure
p1()
BEGIN
select
*
from
student;
INSERT
into
teacher(tname)
values
(
"ct"
);
END
call p1()
cursor
.callproc(
'p1'
)
2. 传参数
in
调用执行时加括号是为了预留传参(
in
,
out
,inout)
delimiter //
create
procedure
p2(
in
n1
int
,
in
n2
int
)
BEGIN
select
*
from
student
where
sid > n1;
END
//
delimiter ;
sql: call p2(12,2)
py:
cursor
.callproc(
'p2'
,(12,2))
3. 参数
out
用以模拟返回值 只能往回传,获取不到传入的值
delimiter //
create
procedure
p3(
in
n1
int
,
inout n2
int
)
BEGIN
set
n2 = 123123;
select
*
from
student
where
sid > n1;
END
//
delimiter ;
sql:
set
@v1 = 10;创建并设置回话级别的变量 创建session级别变量 @
set
设置变量
call p3(12,@v1)
select
@v1; 打印 执行结束后的 全局变量
py:
cursor
.callproc(
'p3'
,(12,2)) 传参
r1 =
cursor
.fetchall() 拿结果集
print(r1)
cursor
.
execute
(
'select @_p3_0,@_p3_1'
) 固定写法 拿取存储过程的执行结果
r2 =
cursor
.fetchall()
print(r2)
(等同于 sql 中的以下操作!
set
@_p3_0 = 12 session 级别的变量
ser @_p3_1 = 2
call p3(@_p3_0,@_p3_1)
select
@_p3_0,@_p3_1
)
|
总结:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
=======>特性:
a. 可传参:
in
out
inout
b. pymysql
1.拿结果集
cursor
.callproc(
'p3'
,(12,2))
r1 =
cursor
.fetchall()
print(r1)
2.拿返回值
cursor
.
execute
(
'select @_p3_0,@_p3_1'
)
r2 =
cursor
.fetchall()
print(r2)
*为什么有结果集又有
out
伪造的返回值?(主要原因:是用于判断执行SQL语句的结果!)
delimiter //
create
procedure
p3(
in
n1
int
,
out
n2
int
设置一个值,用于标识存储过程的执行结果 1,2
)
BEGIN
-- 执行居多的增加语句
insert
into
表名(列名)
values
();
insert
into
表名(列名)
values
();
insert
into
表名(列名)
values
();
insert
into
表名(列名)
values
();
insert
into
表名(列名)
values
();
END
//
delimiter ;
|
事务
事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
判断操作完成状态:执行完成就结束,有错误就回滚。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#伪代码:
delimiter //
create
procedure
p4(
out
status
int
)
BEGIN
1. 声明如果出现异常则执行{
set
status = 1; 检测到错误 返回的状态值
rollback
; 必须加上 回滚操作!
}
开始事务
-- 某1账户减去100 操作
-- 某2账户加90
-- 某3账户加10
commit
; 事物执行完,正确会提交!
结束
set
status = 2; 正常执行成功,返回状态值
END
//
delimiter ;
|
举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
delimiter //
create
PROCEDURE
p5(
OUT
p_return_code tinyint
)
BEGIN
DECLARE
exit handler
for
sqlexception
BEGIN
-- ERROR
set
p_return_code = 1;
rollback
;
END
;
START
TRANSACTION
;
-- 开始事务
DELETE
from
tb1;
insert
into
tb2(
name
)
values
(
'seven'
);
COMMIT
;
-- SUCCESS
set
p_return_code = 2;
END
//
delimiter ;
|
获取out返回结果:
1
2
3
4
5
6
7
8
|
sql:
set
@v2 = 0;
call p5(@v2);
select
@v2;
py:
cursor
.
execute
(
'select @_p5_0'
)
r5 =
cursor
.fetchall()
print(r5)
|
MyISAM 和 InnoDB的区别
MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到写操作完成。另外,MyISAM对于SELECT COUNT(*) 统计 这类的计算是超快无比的。
InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比MyISAM还慢。他是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。
游标 cursor (跟循环有关)
注意:性能不高!对每一行数据,分门别类的要进行计算的时候,才会使用!
1、声明游标
2、获取A表中的数据
my_cursor select id ,num from A
3、循环操作!for row_id,row_num in my_cursor: (不智能,不知道什么时候循环结束)
#检测循环时候还有数据,如果没有数据就退出!
insert into B(num) values (row_id+row_num)
4、要想使用变量得先声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#代码举例
delimiter //
create
procedure
p6()
begin
#声明变量
declare
row_id
int
;
-- 自定义变量1
declare
row_num
int
;
-- 自定义变量2
declare
done
INT
DEFAULT
FALSE
;
declare
temp
int
;
#创建游标
declare
my_cursor
CURSOR
FOR
select
id,num
from
A;
-- 声明游标类型
declare
CONTINUE
HANDLER
FOR
NOT
FOUND
SET
done =
TRUE
;
-- 游标内部 没有值 done设置为True
#开始操作
open
my_cursor;
-- 打开游标
xxoo: LOOP 语法:循环名:LOOP 开始循环
fetch
my_cursor
into
row_id,row_num;
if done
then
leave xxoo;
END
IF;
set
temp
= row_id + row_num;
insert
into
B(number)
values
(
temp
);
end
loop xxoo;
close
my_cursor;
-- 关闭游标
end
//
delimter ;
|
动态执行SQL(防SQL注入)
伪代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
delimiter //
create
procedure
p7(
in
tpl
varchar
(255), # 接收一大堆的字符串
in
arg
int
)
begin
1. 预检测某个东西 SQL语句合法性 做防sql注入的问题
2. SQL =格式化 tpl + arg 把传入的两个参数进行字符串格式化
3. 执行SQL语句
-----> 拿取结果集
set
@xo = arg;
PREPARE
xxx(变量,随便起名字)
FROM
tpl (
'select * from student where sid > ?'
);
EXECUTE
xxx USING @xo;
-- 注意:格式化的时候,传入的值必须是session级别的变量
DEALLOCATE
prepare
xxx;
-- 执行格式化完成的SQL语句
end
//
delimter ;
call p7(
"select * from tb where id > ?"
,9) # ?表示占位符
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
======> 示例 <======
delimiter \\
CREATE
PROCEDURE
p8 (
in
nid
int
)
BEGIN
set
@nid = nid;
PREPARE
prod
FROM
'select * from student where sid > ?'
;
EXECUTE
prod USING @nid;
DEALLOCATE
prepare
prod;
END
\\
delimiter ;
|
数据库相关操作:
1. SQL语句 ***** 优先使用 (要求速度!)
- select xx() from xx ;
2.先查找内置函数,有就用! 利用MySQL内部提供的功能 (性能要求不高!)
索引
索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据。对于索引,会保存在额外的文件中。
作用:
- 约束
- 加速查找
索引分类:
- 主键索引:加速查找 + 不能为空 + 不能重复
- 普通索引:加速查找
- 唯一索引:加速查找 + 不能重复
- 联合索引(多列):
- 联合主键索引
- 联合唯一索引
- 联合普通索引
1、加速查找:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
快:
select
*
from
tb
where
name
=
'asdf'
select
*
from
tb
where
id=999
假设:
id
name
email
...
...
..
无索引:对整个表从前到后依次查找
索引:
id 创建额外文件(某种格式存储)
name
创建额外文件(某种格式存储)
email 创建额外文件(某种格式存储)
create
index
ix_name
on
userinfo3(email);
name
email 创建额外文件(某种格式存储)
|
索引种类(某种格式存储):
hash索引: (存储是对表中的数据进行存储,无序)
单值速度快
范围速度慢 对于连续型的或是有范围的数据会慢,原因是hash表内存储的数据与原表中的数据不对称
btree索引:
二叉树 索引 金字塔结构,从顶到底查找,左支比当前数小,右支比当前数大
========》 结果:快 《========
建立索引:
必须注意的三点:
- a. 额外的文件保存特殊的数据结构 (创建索引会重新创建一个文件,以保存对应的关系)
- b. 查询快;但是执行 插入 更新 删除 操作时慢
- c. 命中索引 (利用创建的索引进行查询,建立了不使用就是浪费)
1
2
|
select
*
from
userinfo3
where
email=
'aaa8888@163.com'
;
select
*
from
userinfo3
where
email
like
'new%'
; 慢 (模糊匹配,索引没用,还是会在原数据中一个一个去找)
|
主键索引:
主键有两个功能:加速查询 和 唯一约束
普通索引:
1
2
|
-
create
index
索引名称
on
表名(列名,)
-
drop
index
索引名称
on
表名
|
唯一索引:
1
2
|
-
create
unique
index
索引名称
on
表名(列名)
-
drop
unique
index
索引名称
on
表名
|
组合索引:(最左前缀匹配)
组合索引是将n个列组合成一个索引。其应用场景为:频繁的同时使用n列来进行查询
遵循:最左前缀匹配规则,跟索引顺序有关,条件中第一个判断必须是建立组合索引的第一个值。
1
2
|
-
create
unique
index
索引名称
on
表名(列名1,列名2)
-
drop
unique
index
索引名称
on
表名
|
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
例: - create index ix_name_email on test(name,email,) - 最左前缀匹配 select * from test where name='alex666'; 会 select * from test where name='alex666' and email='alex666@163.com'; 会 select * from test where email='alex666@163.com'; 不会
组合索引效率 > 索引合并 (具体还是以使用环境决定)
1
2
3
4
5
6
7
8
9
10
11
|
组合索引 (当查询的信息需要多个条件时)
- (
name
,email,)
select
*
from
test
where
name
=
'aaa666'
and
email=
'aaa666@163.com'
; 快
select
*
from
test
where
name
=
'aaa666'
;
索引合并:(平常查询就用一个条件判断,多个条件不常用的情况)
-
name
- email
select
*
from
test
where
name
=
'aaa666'
; 快
select
*
from
test
where
email=
'aaa666@163.com'
; 快
select
*
from
test
where
name
=
'aaa666'
and
email=
'aaa666@163.com'
; 相对快
|
名词:虚拟存在的索引
覆盖索引:
- 直接在索引文件中获取数据 (不是真实的索引)。select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖
1
|
例:
select
email
from
test
where
email=
"aaa2566666@163.com"
|
索引合并:- 把多个单列索引合并使用
1
|
例:
select
email
from
test
where
email=
"aaa2566666@163.com"
and
tid=2222222
|
文本索引:对整个数据表做成索引,不常用到,一般是用第三方软件去调用
注意:
由于索引是专门用于加速搜索而生,所以加上索引之后,查询效率会快到飞起来。
数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。
2、为频繁查找的列创建索引 但是有些语法不会对索引加速
若想快速查询,我们的操作是这样的:
- 创建索引
- 命中索引 *****
大数据量下注意,小数据没事!
但是,以下方式不会命中索引:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
-
like
'%xx'
模糊查找
select
*
from
test
where
email
like
'aaa%'
;
可用第三方工具去加速!
- 使用函数 取反 倒序
select
*
from
test
where
reverse(email) =
'aaa235555@163.com'
;
若想翻转查找,就在python中先把要查询的数据反转,再去和数据库内容匹配
-
or
索引
or
非索引 就无法命中索引操作
select
*
from
test
where
tid = 1
or
name
=
'aaa235555@163.com'
;
特别的:当
or
条件中有未建立索引的列才失效,以下会走索引
select
*
from
test
where
tid = 1
or
name
=
'aaa2666666'
and
email =
'aaa235555@163.com'
#会忽略非索引
- 查找条件,填写的数据类型不一致
如果列是字符串类型,传入条件是必须用引号引起来,若是写成数字类型,这里边就涉及转码的过程,肯定会拖慢查询速度
select
*
from
test
where
email = 999;
- != 不等于 说白了就是取反
select
*
from
test
where
email !=
'aaa'
特别的:如果是主键,则还是会走索引
select
*
from
test
where
tid != 123
- > 大于
select
*
from
test
where
email >
'aaa1111111@163.com'
特别的:如果是主键或索引是整数类型,则还是会走索引
select
*
from
test
where
tid > 123
select
*
from
test
where
num > 123
-
order
by
排序
注意:当根据索引排序时候,选择的映射(要显示的列)如果不是索引,则不走索引
select
name
from
test
order
by
email
desc
;
特别的:如果对主键排序,则还是走索引:
select
*
from
test
order
by
nid
desc
;
- 组合索引最左前缀
如果组合索引为:(
name
,email)
name
and
email
-- 使用索引
name
-- 使用索引
email
-- 不使用索引
|
3、判断时间
explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化
MySQL有执行计划:在真正执行操作之前,先让mysql预估执行操作(一般正确) 看看费时和type类型!好做判断!
1
2
3
4
5
|
语法:explain SQL语句 SQL 预估操作时间(仅作参考)
type类型:
all
<
index
< range < index_merge < ref_or_null < ref < eq_ref < system/const
all
是全局扫描
ref 是索引扫描
|
1
2
3
4
5
6
7
8
9
10
11
|
#操作举例:
#搜索 id,email
慢:
select
*
from
test
where
name
=
'aaa2666666'
explain
select
*
from
test
where
name
=
'aaa2666666'
type:
ALL
(全表扫描)
select
*
from
test limit 1;
#limit 分页获取是个例外,此种操作获取前面的数据会很快(因为操作的数据少啊),若起始是查找后边好几百万的数据行,照样飞不起来!
快:
select
*
from
test
where
email=
'aaa2666666'
type: const(走索引)
|
4、DBA做的那些事儿
慢日志
- 执行时间 > 10
- 未命中索引
- 日志文件路径
配置:(修改配置文件之前,一定要注意先备份!要不然就得跑路!!!)
- 程序执行暂存在内存中,同时配置信息也在!
- 查看当前配置信息:(内存内的变量)
1
2
|
show variables
like
'%query%'
#查看有 query 的变量 (主要是看配置信息,文件路径)
show variables
like
'%queries%'
#查看有 queries 的变量(主要是看索引)
|
- 修改当前配置:
1
2
3
4
5
|
set
global
变量名 = 值
set
slow_query_log =
ON
开启慢日志记录
set
slow_query_log_file = D:\mysql\setup\mysql-5.7.18-winx64\data\ZYJ-PC-slow.log 日志文件 默认在data目录中
set
long_query_time = 2 时间限制,超过此时间,则记录
set
log_queries_not_using_indexes =
ON
为使用索引的搜索记录
|
- 配置文件 写好配置文件,然后再开启数据库的时候一起执行生效,这样就省的在每次开启之后,再去配置!
1
2
3
4
5
6
7
8
9
|
开启执行: mysqld
--defaults-file='配置文件的绝对路径+配置文件名'
创建配置文件
--->随意
配置文件内容:
slow_query_log =
ON
开启慢日志记录
slow_query_log_file = D:\mysql\setup\mysql-5.7.18-winx64\data\ZYJ-PC-slow.log 日志文件 默认在data目录中
long_query_time = 2 时间限制,超过此时间,则记录
log_queries_not_using_indexes =
ON
为使用索引的搜索记录
|
注意:修改配置文件之后,需要重启服务生效(修改只是修改在硬盘内的文件,内容内的文件没有修改!)
5、******分页*******
从数据库中取少量数据,那速度应该还可以,但是如果太庞大或是靠后的话,再去查找就会变慢。所以针对这种情况,我们给出了以下几个方法。
a. 限制页数,只能查看多少页
1
|
select
*
from
test limit 20,10;
|
b. 三种方法
- 直接不让看
- 索引表中扫:(覆盖索引)
1
2
3
4
|
SELECT
*
FROM
test
WHERE
tid
in
(
SELECT
N.tid
FROM
(
SELECT
*
FROM
test LIMIT 2900000,10)
AS
N)
|
- 方案:相对查找
主要是 ------> 记录当前页最大或最小ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
1. 页面只有上一页,下一页
# max_id
# min_id
下一页:tid > max_id
select
*
from
test
where
tid > 2900000 limit 10;
上一页:tid < min_id
SELECT
*
FROM
(
select
*
from
test
where
tid < 2899991
ORDER
BY
tid
DESC
limit 10)
AS
N
ORDER
BY
N.tid
ASC
;
2. 跳页 上一页 192 193 [196] 197 198 199 下一页
SELECT
*
FROM
(
SELECT
*
FROM
(
SELECT
*
FROM
test
WHERE
tid > 2800000 LIMIT 40)
AS
N
ORDER
BY
N.tid
DESC
LIMIT 10)
AS
S
ORDER
BY
S.tid
ASC
|
c. *****为什么不用 between and *****:
最致命的原因:若id不连续,无法直接使用id范围进行查找,获取的数据就不在是定值!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 执行存储过程 cursor.callproc('p1', args=(1, 22, 3, 4)) # 获取执行完存储的参数 cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3") result = cursor.fetchall() conn.commit() cursor.close() conn.close() print(result)