根据产品经理给的原型图,搞他就是。
该类产品一般都有三个端:
从开始提出第一个原型图到第一版本雏形用了三个月。
在公司中要么以要么以内网对远端的服务器进行局部访问。不会提供公网地址————>
避免被黑。
github/码云-------------->开源,即便是收费的私有仓库也不太安全。
自己搭私有服务器。
方式:根据产品原型图一页一页的往下捋,找出需要存储的数据(持久数据),先不分表。
即时通讯:不需要做历史记录。
活体识别:需要截图到后台(后台是人工设别吗?)
1. 主键:
一条数据的唯一标识。
MySQL中若开发人员不定义主键,innoDB会自动创建主键。但是建议自己去申明主键------->不用区分不同数据库引擎。
当使用业务数据为主键,在业务上没问题,需要考虑后期维护。如果涉及到表的关联,需作为外键的字段应该不能被经常修改。(eg:手机号虽然唯一,但是又可能修改)
so: 一般不以业务数据为主键。
2. 一对多,多对多的关系,往往需要新建数据表。
用户<----->频道 ---->多对多
通过第三张表关联
3. 范式与反范式设计
范式:不能存在冗余字段
反范式设计:用空间换时间。
eg:
评论数据:怎么存?------->
文章------->评论(一对多)
select count(*) from comment where article_id=1
有些数据可以从其它表中统计出来----->遵循范式设计。(不存在冗余字段)
首页只需要展示评论数量而不是评论的具体内容,首页有很多文章,每篇文章都要查询评论表再聚合这样聚合设计统计会非常慢。
反范式设计——>提高速率
eg:评论数量
从评论表中执行selecet 语句,效率太低。
4.搜索
时间与状态设计:
今日头条,推文下的时间是推荐时间。---->不是文章发布时间
文章创建时间、修改时间、审核时间、
文章状态------->待审核、审核中、审核通过、草稿(互斥status(1234))
新闻置顶:后台统一控制。
封面图片:0-3张
封面若是放在文章表中——1.浪费资源 2. 业务改变时需要修改表(产品经理调整需要扩容)。
------>新建一张表。
mysql5.7之后新增了json类型数据。本质上仍是长字符串。
cover = '{'type':3,'pics':['url1','url2','url3']}'
修改机会特别少。
表的复用与自关联
1.主键:bigint,unsigned
is_delete(底层一般没有bool真假类型):tinyint(-128-127/0-255)
对于整型:
unsigned int(3) /unsigned int(6): 存储范围一样大,3/6控制的是显示长度。不足位的时候补0。
2.手机号:字符串
show create table news_article_basic; #查看创表语句
3.索引:
4. 外键:
申明外键:
ALTER TABLE 'user_resource' CONSTRAINT 'FKEEAF1E02D82D57F9' FOREIGN KEY('user_id')REFERENCE 'sys_user' ('id')
# 没有选项,默认是 no action
[ON DELETE reference_option]
[ON UPDATE reference_option]
1. CASCADE(cascade)——表
在父表上update/delete记录时,同步update/delete子表的匹配记录。
2. SET NULL(set null)——记录
在父表上update/delete记录时,将子表上匹配记录的列设为null (子表的外键列不能为not null)
3. NO ACTION——记录
如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
4. RESTRICT
同no action, 都是立即检查外键约束
5. SET DEFAULT
父表有变更时,子表将外键列设置成一个默认的值 但Innodb目前不支持
notes:
show ENGINES # 查看所有引擎
show VARIALES LIKE 'storage_engine'; # 查看默认引擎
1. InnoDB
特点:
1、InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事物安全(ACID兼容)存储引擎。
InnoDB锁定在行级并且也在SELECT语句中提供一个类似Oracle的非锁定读。这些功能增加了多用户部署和性能。在SQL查询中,可以自由地将InnoDB类型的表和其他MySQL的表类型混合起来,甚至在同一个查询中也可以混合。
2、InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘的关系型数据库引擎锁不能匹敌的
3、InnoDB存储引擎完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB将它的表和索引在一个逻辑表空间中,表空间可以包含数个文件(或原始磁盘文件)。这与MyISAM表不同,比如在MyISAM表中每个表被存放在分离的文件中。InnoDB表可以是任何尺寸,即使在文件尺寸被限制为2GB的操作系统上
4、InnoDB支持外键完整性约束
5、存储表中的数据时,每张表的存储都按主键顺序存放,如果没有显示在表定义时指定主键,InnoDB会为每一行生成一个6字节的ROWID,并以此作为主键
6、InnoDB被用在众多需要高性能的大型数据库站点上
InnoDB不创建目录,使用InnoDB时,MySQL将在MySQL数据目录下创建一个名为ibdata1的10MB大小的自动扩展数据文件,以及两个名为ib_logfile0和ib_logfile1的5MB大小的日志文件
MyISAM存储引擎
MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事物。MyISAM主要特性有:
1、大文件(达到63位文件长度)在支持大文件的文件系统和操作系统上被支持
2、当把删除和更新及插入操作混合使用的时候,动态尺寸的行产生更少碎片。这要通过合并相邻被删除的块,以及若下一个块被删除,就扩展到下一块自动完成
3、每个MyISAM表最大索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16
4、最大的键长度是1000字节,这也可以通过编译来改变,对于键长度超过250字节的情况,一个超过1024字节的键将被用上
5、BLOB和TEXT列可以被索引
6、NULL被允许在索引的列中,这个值占每个键的0~1个字节
7、所有数字键值以高字节优先被存储以允许一个更高的索引压缩
8、每个MyISAM类型的表都有一个AUTO_INCREMENT的内部列,当INSERT和UPDATE操作的时候该列被更新,同时AUTO_INCREMENT列将被刷新。所以说,MyISAM类型表的AUTO_INCREMENT列更新比InnoDB类型的AUTO_INCREMENT更快
9、可以把数据文件和索引文件放在不同目录
10、每个字符列可以有不同的字符集
11、有VARCHAR的表可以固定或动态记录长度
12、VARCHAR和CHAR列可以多达64KB
使用MyISAM引擎创建数据库,将产生3个文件。文件的名字以表名字开始,扩展名之处文件类型:frm文件存储表定义、数据文件的扩展名为.MYD(MYData)、索引文件的扩展名时.MYI(MYIndex)
MEMORY存储引擎
MEMORY存储引擎将表中的数据存储到内存中,未查询和引用其他表数据提供快速访问。MEMORY主要特性有:
1、MEMORY表的每个表可以有多达32个索引,每个索引16列,以及500字节的最大键长度
2、MEMORY存储引擎执行HASH和BTREE缩影
3、可以在一个MEMORY表中有非唯一键值
4、MEMORY表使用一个固定的记录长度格式
5、MEMORY不支持BLOB或TEXT列
6、MEMORY支持AUTO_INCREMENT列和对可包含NULL值的列的索引
7、MEMORY表在所由客户端之间共享(就像其他任何非TEMPORARY表)
8、MEMORY表内存被存储在内存中,内存是MEMORY表和服务器在查询处理时的空闲中,创建的内部表共享
9、当不再需要MEMORY表的内容时,要释放被MEMORY表使用的内存,应该执行DELETE FROM或TRUNCATE TABLE,或者删除整个表(使用DROP TABLE)
提供提交、回滚、崩溃恢复能力的事物安全(ACID兼容)能力,并要求实现并发控制,InnoDB是一个好的选择
数据表主要用来插入和查询记录,则MyISAM引擎能提供较高的处理效率
临时存放数据,数据量小,不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果
只有INSERT和SELECT操作,可以选择Archive,Archive支持高并发的插入操作,但是本身不是事务安全的。Archive非常适合存储归档数据,如记录日志信息可以使用Archive
一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求,使用合适的存储引擎,将会提高整个数据库的性能
作用
使用ORM的方式选择
1. 先创建模型类,再迁移到数据库中
优点:
缺点:
2. 先用原生SQL创建数据库表,再编写模型类作映射
优点:
缺点:
SQLAlchemy—> python 、开源、提供SQL工具包+对象关系映射,使用MIT许可证发行。
1.SQLALCHEMY_DATABASE_URI 数据库的连接信息
2.SQLALCHEMY_TRACK_MODIFICATIONS(sqlalchemy_track_modifications) 在Flask中是否追踪数据修改。
3.SQLALCHEMY_ECHO (sqlalchemy_echo)显示生成的SQL语句,可用于调试。
这些配置参数需要放在Flask的应用配置(app.config)中
from flask import Flask
app = Flask(__name__)
# 配置对象
class Config(object):
SQLALCHEMY_DATABASE_URL = 'mysql://root:[email protected]:3306/toutiao'
SALALCHEMY_TRACK_MODIFICATIONS = False # 一般都为False
SALALCHEMY_ECHO = True # 调式模式下为True,开发模式下:False
class config.from_object(config)
模型类的映射:
from flask_sqlalchemy import SQLAlchemy
# 1. 创建SQLAlchemy对象
db=SQLAlchemy(app) # sqlalchemy对象接收app——因为数据库的连接信息在app中
# 2.定义模型类
class User(db.Model):
"""
用户基本信息
"""
__tablename__ = 'user_basic'
class STATUS:
ENABLE = 1
DISABLE = 0
id = db.Colunm('empliyee_id',)
创建SQLAlchemy对象有两种方式:
方式一:
db = SQLAlchemy(app)
方式二:
db = SQLAlchemy()
db.init_app(app)
该方式需要在上下文(cotext)中使用。
environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'}
with app.request_context(environ):
try:
user = User(mobile='18911111111', name='itheima')
db.session.add(user)
db.session.flush() # 将db.session记录的sql传到数据库中执行
profile = UserProfile(id=user.id)
db.session.add(profile)
db.session.commit()
except:
db.session.rollback()
原生SQL创表,映射模型类:
from flask import Flask
app = Flask(__name__)
class config(object):
SQLALCHEMY_DATABASES_URL='mysql://root:password@localhost/employees'
SQLALCHEMY_TRACK_MODFICAYIONS=False
SQLALCHEMY_ECHO=True
app.config.from_object(config)
# 数据库中已有原生数据表
CREATE TABLE `user_basic` (
`user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`email` varchar(20) COMMENT '邮箱',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态,是否可用,0-不可用,1-可用',
....
`read_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '累计阅读人数',
PRIMARY KEY (`user_id`),
UNIQUE KEY `mobile` (`mobile`),
UNIQUE KEY `user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户基本信息表';
CREATE TABLE `user_profile` (
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户ID',
...
`career` varchar(20) COMMENT '职业',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户资料表';
模型类:
class User(db.Model):
"""
用户基本信息
"""
__tablename__ = 'user_basic'
# 该类用于枚举常量起别名
class STATUS:
ENABLE = 1
DISABLE = 0
id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
mobile = db.Column(db.String, doc='手机号')
password = db.Column(db.String, doc='密码')
...
email = db.Column(db.String, doc='邮箱')
status = db.Column(db.Integer, default=1, doc='状态,是否可用')
class UserProfile(db.Model):
"""
用户资料表
"""
__tablename__ = 'user_profile'
class GENDER:
MALE = 0
FEMALE = 1
id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
gender = db.Column(db.Integer, default=0, doc='性别')
....
career = db.Column(db.String, doc='职业')
模型类语法:( 区别django)
ctime = db.Column('create_time', db.DateTime, default=datetime.now, doc='创建时间')
utime = db.Column('update_time', db.DateTime, default=datetime.now, onupdate=datetime.now, doc='更新时间')
if user.satus == 0:
pass
if arti....# 问题在于用户并不知道你1.2是什么。
解决办法---->在模型类中再定义一个类------>(打包一组数据)枚举可能情况(常量)给其取别名
class STATUS:
ENABLE = 1
DISABLE = 0
...
if user1.status == User.STATUS.DISABLE: # 等价于:user.satus == 0
...
用字典打包数据:
STATUS = {
ENABLE = 1,
DISABLE = 0
}
...
if user1.status == User.STATUS.['DISABLE']
创建SQLAlchemy对象方式二:
db=SQLAlchemy()
db.init_app(app)
tips:传app的原因是因为,数据库的连接信息在app中。
第二种方法,在创建SQLAlchemy创建时,又可能flask对象又可能还没创建出来,所以需要调用init_app(app)传入flask对象。
区别:
因为第一种方法,在SQLAlchemy对象创建时就从flask对象中把配置文件拿到手了。
第二种方法
此方法在单独运行调试时,对数据库的操作需要在Flask的应用上下文中进行:
with app.app_context():
User.query.all()
django提供了数据库的操作,所以可直接是由ORM而flask需要SQLAlchemy软件。
概念:
单个对象添加:
多个对象批量添加:
user = User(mobile='123xxxxxxxx',...)
db.session.add(user)
db.session.commit()
# //
db.session.add(user1,user2,...)
db.session.commit()
all()
first()
get()
select (这里写所有字段名而不是*)from User where mobile=‘123456789’ and user_id=3;
1. filter_by
User.query.filter_by(mobile=‘123456789’,user_id=3).all()
2. filter
User.query.filter(User.mobile==‘123456789’,User.user_id==3).all()
3. or_
from sqlachemy import or_
User.query.filter(or_(User.mobile==‘123456789’,User.user_id==3)).all()
4.and_
from sqlalchemy import and_
User.query.filter(and_(User.name != ‘13911111111’, User.mobile.startswith(‘185’))).all()
5. not_
from sqlachemy import not_
User.query.filter(not_(User.mobile == ‘13911111111’)).all()
6.offset
User.query.filter(User.name.startswith(‘13’)).order_by(User.id.asc()).offset(2).limit(5).all()
User.query.order_by(User.id.desc())
----->一个SQLAlchemy的查询对象,这个对象缓存了将要转化的SQL语句。.all()or.first()才执行sql语句查询符合条件的对象。user = User.query.filter_by(id=1).first() # 查询所有字段
select user_id, mobile......
select * from # 程序不要使用
select user_id, mobile,.... # 查询指定字段
from sqlalchemy.orm import load_only
User.query.options(load_only(User.name, User.mobile)).filter_by(id=1).first() # 查询特定字段
查询结果中有不存在于模型类中的字段------->无法指定模型类对象。
查询每个用户关注数:
from sqlalchemy import func
db.session.query(Relation.user_id, func.count(Relation.target_user_id)).filter(Relation.relation == Relation.RELATION.FOLLOW).group_by(Relation.user_id).all()
结果:[(),(),()…]
主模型类:User
子模型类:UserProfile
1. 使用ForeignKey(前提是数据库表中指明了外键)
主键表&外键表
外–(访问)—>主:外键唯一确定–>从ForeingKey(‘数据库表.主键字段’)
主---------------->外:额外字段
字段名 = db.relationship(‘类’,uselist=Flase)
一对一:uselist=False---->返回一个对象
默认返回一个列表。
若不做额外申明,relationship为惰性查询,需要获取外键表的字段时,才查询外键表。
# User
profile = db.relationship(' UserProfile', uselist=False)
#查询
user.profile.query.get(3)
...
FROM user_profile
WHERE user_profile.user_id = 3
user.profile------->得到的是relationship的UserProfile对象
2. 使用primaryjoin(若数据库中根本就没有指定外键这个字段)
在主键表中,指定主键字段与另一张表某字段相同。
profile = db.relationship(‘Userprofile’,primaryjion=‘User.id==foreign(UserProfile.id)’, uselist=False)
3. 指定字段关联查询
需求:查询所关注的用户的名字。
select from user_basic as a inner jion user_relation as on a.user_id=b.target_user_id where b.target_user_id=2
from sqlalchemy.orm import load_only
from sqlalchemy.orm import contains_eager
# Relation
user = db.relationship('User',rimaryjoin=='Relation.target_user_id = foreign(User.id)')
# 查询语句
Relation.query.join(Relation.user).options(load_only(Relation.target_user_id), contains_eager(Relation.user).load_only(User.name)).all()
结果:[
列表
取值:list[0].属性
list[0].user------>得到的是relationship的User对象。
user = User.query.get(1)
user.name = 'Python'
db.session.add(user)
db.session.commit()
User.query.filter_by(id=1).update({'name':'python'})
db.session.commit()
user = User.query.order_by(User.id.desc()).first()
db.session.delete(user)
db.session.commit()
User.query.filter(User.mobile='18512345678').delete()
db.session.commit()
flask:事务默认开启
在事务开启到事务提交之间的内容------>一个事务
db.session.commit():提交事务
environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'}
with app.request_context(environ):
try:
user = User(mobile='18911111111', name='itheima')
db.session.add(user)
db.session.flush() # 将db.session记录的sql传到数据库中执行
profile = UserProfile(id=user.id)
db.session.add(profile)
db.session.commit()
except:
db.session.rollback()
数据库服务器集群:
主:写操作(产生有效数据的操作)
从:读操作(不产生有效数据)
复制集:一主多从。舵主多从(多主之间互为复制关系)
分布式集群:所有主构成完整数据,为了提高“可用性”一般会给每个分片配一个从服务器。
MySQL主从依赖MySQL自己实现
主从同步的作用:
思考——读写分离对事务是否有影响???
答:可能会有影响。
对于写操作包括开启事务和提交或回滚要在一台机器上执行,分散到多台master执行后数据库原生的单机事务就失效了。
——对于写操作,最好在一台机器上执行。
对于事务中同时包含读写操作,与事务隔离级别(在一个事务中做了修改对其它事务是否可见)设置有关,如果事务隔离级别为read-uncommitted() 或者 read-committed,读写分离没影响,如果隔离级别为repeatable-read、serializable,读写分离就有影响,因为在slave上会看到新数据,而正在事务中的master看不到新数据。
事务隔离级别 必背!
事务A,事务B
方法:
RANGE
按主键范围进行划分
比如:从0到10000一个表,10001到20000一个表;
HASH取模 离散化
一个商场系统,一般都是将用户,订单作为主表,然后将和它们相关的作为附表,这样不会造成跨库事务之类的问题。 取用户id,然后hash取模,分配到不同的数据库上。
比如x%3 =1 在第一台。=2…
比如按照华东,华南,华北这样来区分业务,七牛云应该就是如此。
按照时间切分,就是将6个月前,甚至一年前的数据切出去放到另外的一张表,因为随着时间流逝,这些表的数据 被查询的概率变小,所以没必要和“热数据”放在一起,这个也是“冷热数据分离”。
事务支持
分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价; 如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。
多库结果集合并(group by,order by)
跨库join
分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表, 结果原本一次查询能够完成的业务,可能需要多次查询才能完成。 粗略的解决方法: 全局表:基础数据,所有库都拷贝一份。 字段冗余:这样有些字段就不用join去查询了。 系统层组装:分别查询出所有,然后组装起来,较复杂。
问题:水平分库分表——表中的字段都是一样的,知识值放在了不同表、库、服务器中。主键是否会受到影响???——有可能会(比如主键采用自增的方式,当把所有表何在一起时,主键会重复)
解决——分布ID
保证进行分库分表之后,从整体考虑数据库主键仍是唯一。
1. UUID
Universally Unique Identifier
由128位二进制组成,一般转换成十六进制,然后用String表示。
优点:
缺点:
2.数据库主键自增
缺点:过于依赖中心数据库,中心数据库宕掉,整个业务就瘫掉了。
第一台 start 1 step 9
第二台 start 2 step 9
第三台 start 3 step 9
优点:
简单方便,有序递增,方便排序和分页
缺点:
没办法保证扩容。
3.Redis
redis 中的字符串——string set id 100---->再redis中如果字符串中的数据是个数字的话,redis会自动把它当作一个整型进行对待。
Incr id :自动对id进行累加。调一次Incr id增加一次
IncrBy:
redis是单线程,能保证原子性。
缺:
中心库,redis挂就挂
内存型数据库,关机数据可能丢失。
4.雪花算法-Snowflake(现成的轮子)——强依赖时间
推特公司开源——雪花算法。
64位的整数,代表ID
1bit:第一位符号位
41bit:时间戳(可记录69年)
10bit:记录机器id,总共记录1024台机器,一般前五位代表数据中心,后5位某数据中心机器id
12bit:循环位,用来对同一个毫秒之内产生不同的ID,12位可以最多记录4095个,也就是在同一个机器同一毫秒最多记录4095个,多余的需要进行等待下毫秒。
上面只是一个将64bit划分的标准,当然也不一定这么做,可以根据不同业务的具体场景来划分,比如下面给出一个业务场景:
服务目前QPS10万,预计几年之内会发展到百万。
当前机器三地部署,上海,北京,深圳都有。
当前机器10台左右,预计未来会增加至百台。
这个时候我们根据上面的场景可以再次合理的划分62bit,QPS几年之内会发展到百万,那么每毫秒就是千级的请求,目前10台机器那么每台机器承担百级的请求,为了保证扩展,后面的循环位可以限制到1024,也就是2^10,那么循环位10位就足够了。
“1符号 41时间戳 (前2不变) 3机房7机器ID 10循环位2扩展”
5.时钟回拨——隐含问题
因为机器的原因会发生时间回拨,我们的雪花算法是强依赖我们的时间的,如果时间发生回拨,有可能会生成重复的ID。
解决——记录上次时间戳,将本次时间戳与上次记录对比,若本次小于上次(发生时间回拨)——抛出异常。
避免全表扫描,应考虑在 where 及 order by 涉及的列上建立索引;
查询时使用select明确指明所要查询的字段,避免使用select *的操作;
SQL语句尽量大写,如
SELECT name FROM t WHERE id=1
对于小写的sql语句,通常数据库在解析sql语句时,通常会先转换成大写再执行。
尽量避免在 where 子句中使用!=或<>操作符, MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE;
SELECT id FROM t WHERE name LIKE ‘abc%’
对于模糊查询,如:
SELECT id FROM t WHERE name LIKE ‘%abc%’
或者
SELECT id FROM t WHERE name LIKE ‘%abc’
将导致全表扫描,应避免使用,若要提高效率,可以考虑全文检索;
遵循最左原则,在where子句中写查询条件时把索引字段放在前面,如
mobile为索引字段,name为非索引字段
推荐
SELECT … FROM t WHERE mobile=‘13911111111’ AND name=‘python’
不推荐
SELECT … FROM t WHERE name=‘python’ AND mobile=‘13911111111’
建立了复合索引 key(a, b, c)
推荐
SELECT … FROM t WHERE a=… AND b=… AND c= …
SELECT … FROM t WHERE a=… AND b=…
SELECT … FROM t WHERE a=…
不推荐 (字段出现顺序不符合索引建立的顺序)
SELECT … FROM t WHERE b=… AND c=…
SELECT … FROM t WHERE b=… AND a=… AND c=…
…
能使用关联查询解决的尽量不要使用子查询,如
子查询
SELECT article_id, title FROM t_article WHERE user_id IN (SELECT user_id FROM t_user WHERE user_name IN (‘itcast’, ‘itheima’, ‘python’))
关联查询(推荐)
SELECT b.article_id, b.title From t_user AS a INNER JOIN t_article AS b ON a.user_id=b.user_id WHERE a.user_name IN (‘itcast’, ‘itheima’, ‘python’);
能不使用关联查询的尽量不要使用关联查询;
不需要获取全表数据的时候,不要查询全表数据,使用LIMIT来限制数据。