Django之深入了解ORM

Django ORM操作

常用字段

models中所有的字段类型其实本质就那几种,整形varchar什么的,都没有实际的约束作用,虽然在models中没有任何限制作用,但是还是要分门别类,对于校验性组件校验非常有用
就比如说邮箱类型,你在输入邮箱的时候如果不按照邮箱格式输入,瞎鸡儿输入会提示你不合法,虽然输入的是字符串,但是不是规定的邮箱字符串

字段 描述
AutoField int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
IntegerField 一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)
BigIntegerField
CharField 字符类型对应MySQL的varchar类型,必须提供max_length参数, max_length表示字符长度。
DateField 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。
DecimalField 10进制小数, max_digits,小数总长度 ,decimal_places,小数位长度
EmailField email字段,内部还是varchar(254)
BooleanField 布尔值,传True或False,自动转成1/0
TextField 存储大段文本
FileField 专门用来存放文件路径,传值的时候,直接传文件对象,将路径保存到数据库
关系字段 描述
ForeignKey 关系字段,外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
OneToOneField 一对一字段。
ManyToManyField 多对多表关系并且这一张多对多的关系表是有Django自动帮你建

常用字段参数

参数 描述
null 用于表示某个字段可以为空。
unique 如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index 如果db_index=True 则代表着为此字段设置索引。
default 为该字段设置默认值。
DateField和DateTimeField 描述
auto_now_add 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now 配置上auto_now=True,每次更新数据记录的时候会更新该字段。
关系字段参数 描述
to 设置要关联的表
to_field 设置要关联的表的字段
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。
db_constraint 是否在数据库中创建外键约束,默认为True。

自定义字段

Django中的CharField对应的MySQL数据库中的varchar类型,没有设置对应char类型的字段,

但是Django允许我们自定义新的字段,下面我来自定义对应于数据库的char类型

自定义字段在实际项目应用中可能会经常用到

from django.db import models

# Create your models here.
#Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
    '''
    自定义的char类型的字段类
    '''
    def __init__(self,max_length,*args,**kwargs):
        self.max_length=max_length
        super().__init__(max_length=max_length,*args,**kwargs)

    def db_type(self, connection):
        '''
        限定生成的数据库表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        '''
        return 'char(%s)'%self.max_length
    
#应用上面自定义的char类型
class Class(models.Model):
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=32)
    class_name=FixCharField(max_length=16)

字段合集和对应关系

我们来创建一张表:

models.py:
class Books(models.Model):
    title = models.CharField(max_length=254)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField()

将表同步到MySQL中:
python3 manage.py makemigrations
python3 manage.py migrate

然后我们在django测试文件中,如果单纯的测试某个py文件,需要手动配置测试脚本

app01/tests.py:
# Create your tests here.
import os
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")
    import django
    django.setup()
    from app01 import models
    # 下面开始写测试的代码。
    
    
    # 创建数据------------------------------------------------------------------------
    # 1. create 方法
    models.Books.objects.create(title='三国演义',price=123,publish_date='2019-11-11')

    from datetime import date
    ctime = date.today()
    models.Books.objects.create(title='红楼梦',price=888,publish_date=ctime)

    # 2. 利用对象的绑定方法创建数据
    book_obj = models.Books(title='西游记',price=666,publish_date='2000-11-11')
    book_obj.save()
    # 插入的数据
    # id    title     price   publish_date
    # 4	    三国演义	123	    2019-11-11
    # 5	    红楼梦	    888	    2019-11-27
    # 6	    西游记	    666	    2000-11-11

    # 修改数据------------------------------------------------------------------------
    '''
        利用filter 自动查询当前表的主键字段,pk代表primary_key
        filter查询出来的是一个queryset对象:
            只要是queryset的对象就可以无限制的调用queryset的方法
            只要是queryset的对象就可以点query查看当前结果内部对应的sql语句
    '''
    # 1. 利用queryset方法修改数据
    models.Books.objects.filter(pk=4).update(price=444)
    # 2. 利用对象方法修改数据
    book_obj = models.Books.objects.get(pk=1)
    book_obj.price = 222
    book_obj.save()     # 不推荐使用,推荐使用queryset方法
    '''
        利用对象修改,内部其实是将所有的字段重新写进去
        get 和 filter的区别:
            filter 获取到的是一个queryset对象,类似于一个列表,没有数据的时候不会报错
            get获取到的数据就是数据本身,但是没有数据的时候会报错
    '''

    # 删除数据------------------------------------------------------------------------
    # 1. 利用queryset方法 :delete()
    models.Books.objects.filter(pk=4).delete()
    # 2. 对象的方法
    book_obj = models.Books.objects.get(pk=4)
    book_obj.delete()

    
    
    
    
# 如果你想直接查看所有的orm语句内部对应的sql语句,可以在配置文件中配置: 
settings.py:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

单表操作

方法(加括号使用) 描述
all 查询所有,返回QuerySet对象
filter 筛选,相当于原生SQL的where条件,返回QuerySet对象
get 筛选,获取数据对象本身,如果不存在报错,并且查询条件必须是唯一的。
first 取QuerySet对象中第一个数据对象
last 取QuerySet对象中最后一个数据对象
count 统计数据的条数
values 获取数据对象中指定的字段的值,返回列表套字典的QuerySet对象
values_list 获取数据对象中指定的字段的值,返回列表套元组的QuerySet对象
order_by 指定字段进行排序,默认升序,在字段前加-是降序
reverse 颠倒顺序,前提是颠倒的对象必须有顺序,也就是order_by过后的
exclude 查询排除什么之外,返回QuerySet对象
exists 判断查询结果是否有值,返回布尔值
distinct 对指定字段查询出来的结果进行去重操作
# 查询数据 13 条------------------------------------------------------------------------
# 1. all()  查询所有,返回queryset对象
print(models.Books.objects.all())

# 2. filter()   筛选 相当于原生sql语句里面的where关键字,返回queryset对象
print(models.Books.objects.filter(pk=4))

# 3. get()  筛选  获取的数据是对象本身,数据不存在直接报错,并且查询条件必须唯一,返回数据对象本身
print(models.Books.objects.get(pk=4))

# 4. first()    获取queryset对象中第一个数据对象    返回数据对象
print(models.Books.objects.filter(pk=4).first())

# 5. last()     获取queryset对象中最后一个数据对象    返回数据对象
print(models.Books.objects.filter(pk=4).last())

# 6. count()    统计数据的条数     返回数字
print(models.Books.objects.count())

# 7. values()   获取数据对象中指定的字段的值,可以指定多个,返回queryset对象,列表套字典
print(models.Books.objects.values('title','price'))

# 8. values_list()  获取数据对象中指定的字段的值,可以指定多个,返回queryset对象,列表套元组
print(models.Books.objects.values_list('title','price'))

# 9. order_by()   按照指定的字段排序,默认是升序,在字段前面加 - 是降序
print(models.Books.objects.order_by('pk').values('pk'))
print(models.Books.objects.order_by('-pk').values('pk'))

# 10. reverse()     颠倒顺序,前提是颠倒的对象必须有顺序,排序之后的
print(models.Books.objects.order_by('pk').reverse().values('pk'))

# 11. exclude()     查询条件排除什么
print(models.Books.objects.exclude(pk=4).values('pk'))

# 12. exists()      判断查询结果是否有值,返回的是布尔值
print(models.Books.objects.exists())

# 13. distinct()    对查询结果去重,前提是数据完全相同的情况下
print(models.Books.objects.values('title','price').distinct())
    

双下划线查询

方法 描述
__gt 字段大于什么什么值
__lt 字段小于什么什么值
__gte 字段大于等于什么值
__lte 字段小于等于什么值
__in 字段是什么或什么的值,or的关系
__range 字段是什么和什么之间的值,btwen..and...
__year 字段的年份是什么什么的值
__month 字段的月份是什么什么的值
# 下划线查询------------------------------------------------------------------------
# 1. 查询价格大于500的书
print(models.Books.objects.filter(price__gt=500).values('title'))

# 2. 查询价格小于500的书籍
print(models.Books.objects.filter(price__lt=500).values('title'))

# 3. 查询价格大于等于500的书籍
print(models.Books.objects.filter(price__gte=500).values('title'))

# 4. 查询价格小于等于500的书籍
print(models.Books.objects.filter(price__lte=500).values('title'))

# 5. 查询价格是222 或 444 或 500 的书籍
print(models.Books.objects.filter(price__in=[222,444,500]).values('title'))

# 6. 查询价格在200到800之间的书籍      顾头也顾尾
print(models.Books.objects.filter(price__range=(200,800)).values('title'))

# 7. 查询出版日期是2019年的书籍
print(models.Books.objects.filter(publish_date__year='2019').values('title'))

# 8. 查询出版日期是11月份的书籍
print(models.Books.objects.filter(publish_date__month='11').values('title'))

模糊查询

MySQL中的模糊查询,关键字like,模糊匹配的符号:%_

方法 描述
__startswith 字段的值以什么什么开头的值
__endswith 字段的值以什么什么结尾的值
__contanins 字段的值包含什么什么的值,区分大小写
__icontanins 字段的值包含什么什么的值,不区分大小写
# 模糊查询------------------------------------------------------------------------
# 1. 查询书籍是以三开头的书
print(models.Books.objects.filter(title__startswith='三').values('title'))

# 2. 查询书籍是以三结尾的书
print(models.Books.objects.filter(title__endswith='三').values('title'))

# 3. 查询书籍名称中包含游字的书籍
print(models.Books.objects.filter(title__contains='游').values('title'))

# 4. 查询书籍名称中包含字母p的书籍,区分大小写
print(models.Books.objects.filter(title__contains='p').values('title'))

# 5. 查询书籍名称中包含字母p的书籍,区分大小写
print(models.Books.objects.filter(title__icontains='p').values('title'))

多表操作

一对多字段数据的操作

为了方便操作,我们创建图书管理系统相关表来演示多表操作

models.py:

class Book(models.Model):
    title = models.CharField(max_length=254)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
    '''
    auto_now :每次修改数据的时候,会自动更新时间
    auto_now_add :当数据创建出来的时候,自动将创建时间记录下来
    '''
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')

class Publish(models.Model):
    name = models.CharField(max_length=254)
    addr = models.CharField(max_length=254)

class Author(models.Model):
    name = models.CharField(max_length=254)
    email = models.EmailField(max_length=254)
    author_detail = models.OneToOneField(to='AuthorDetail')

class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    add = models.CharField(max_length=254)

# 一对多字段数据的增删改查------------------------------------------------------------------------
# 一对一字段的数据的修改和一对多一样的。
# 增
# 第一种方式:
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='红楼梦',price=444,publish=publish_obj)
# 第二种方式:
models.Book.objects.create(title='三国演义',price=222,publish_id=1)

# 改
# 第一种方式:
models.Book.objects.filter(pk=2).update(publish_id=2)
# 第二种方式:
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)

# 删 默认就是级联删除,级联更新
models.Publish.objects.filter(pk=1).delete()

多对多字段数据的操作

方法 描述
add 能够在第三张表中添加数据既支持传数字,也支持传对象,可以传多个值
set 修改第三张表中的数据,需要传一个可迭代对象进去例如元组,列表,容器类元素可以是数字或者对象
remove 删除第三张表中的数据,既支持传数字,也支持传对象,可以传多个值
clear 清空 删除书籍相关的记录,下线,括号内不需要传递参数
# 多对多字段数据的增删改查------------------------------------------------------------------------
# 增
book_obj = models.Book.objects.filter(pk=2).first()
# print(book_obj.authors)
# app01.Author.None 表示着已经跨到第三张表了
# 给当前这一本书绑定作者,可以写多个
book_obj.authors.add(1)
book_obj.authors.add(1,2)

# 也可以增加对象
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.add(author_obj1,author_obj2)

'''
    add 方法:
        能够在第三张表中添加数据
        既支持传数字,也支持传对象,可以传多个值
    '''

# 改
# 修改id为2的书籍的作者
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.set((1,))
book_obj.authors.set((1,2))

# 也可以修改为对象
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set((author_obj1,author_obj2))

'''
    set 方法:
        修改第三张表中的数据,
        需要传一个可迭代对象进去例如元组,列表,
        容器类元素可以是数字或者对象
    '''

# 删
# 删除id为2的书籍的作者
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.author.remove(1,2)

# 也可以传对象
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.author.remove(author_obj1, author_obj2)

'''
    remove 方法:
        删除第三张表中的数据,既支持传数字,也支持传对象,可以传多个值
    '''

# 清空:
# 删除id为2 的书籍(包括作者信息也删除)
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.clear()

'''
    clear 方法:
        清空 删除书籍相关的记录,下线,括号内不需要传递参数
    '''

跨表查询

在使用跨表查询的时候,要注意一点,

要明白关系字段在哪一方?

从有关系字段的那一方查的话就是正向查询,查询按关系字段查询查。

从没有关系字段的那一方查的话就是反向查询,查询按小写的表名查。

基于对象的跨表查询

方法 描述
all() 当正向查询点击外键字段数据有多个的情况下也就是多对多查询,需要加.all()
_set 反向查询的时候 表名小写并且加_set

子查询

# 跨表查询------------------------------------------------------------------------
# 基于对象的跨表查询
# 1. 查询书籍主键为2的出版社名称
# 正向查询,先查书籍对象,一对多查询
book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.publish.name)

# 2. 查询书籍主键为2的作者姓名
# 正向查询,先查书籍对象,多对多查询
book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.authors)     # app01.Author.None
print(book_obj.authors.all().values('name'))

# 3. 查询作者是qinyj的手机号码
# 正向查询,先查作者对象,一对一查询
author_obj = models.Author.objects.filter(name='qinyj').first()
print(author_obj.author_detail.phone)

'''
    什么时候需要加 all:
        当正向查询点击外键字段数据有多个的情况下
        也就是多对多查询,需要加.all()
    '''

# 4. 查询出版社是东方出版社出版过的书籍
# 反向查询,先查出版社对象,一对多查询
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set.all().values('title'))

# 5. 查询作者是qinyj写过的书籍
# 反向查询,先查作者对象,多对多查询
author_obj = models.Author.objects.filter(name='qinyj').first()
print(author_obj.book_set.all().values('title'))

# 6. 查询手机号是111111的作者姓名
# 反向查询,先查作者详情表的对象,一对一查询
author_detail_obj = models.AuthorDetail.objects.filter(phone='111111').first()
print(author_detail_obj.author.name)

'''
    什么时候反向查询的时候表名小写并且加_set
        一对多
        多对多
        一对一不需要加
    '''

基于双下划线的跨表查询

联表查询

双下划线,链式操作:__

# 基于双下划线的跨表查询 联表操作------------------------------------------------------------------------
# 1. 查询书籍id为2的出版社名称
# 正向查询:
# values中写外键名就相当于跨到外键字段所在的表中了
print(models.Book.objects.filter(pk=2).values('publish__name'))
# 反向查询:
print(models.Publish.objects.filter(book__pk=2).values('name'))

# 2. 查询书籍id为2的作者的姓名
# 正向查询:
print(models.Book.objects.filter(pk=2).values('authors__name'))
# 反向查询:
print(models.Author.objects.filter(book__pk=2).values('name'))

# 3. 查询作者是qinyj的地址
# 正向查询:
print(models.Author.objects.filter(name='qinyj').values('author_detail__add'))
# 反向查询:
print(models.AuthorDetail.objects.filter(author__name='qinyj').values('add'))

# 4. 查询出版社是东方出版社出版过的书的名字
# 正向查询:
print(models.Book.objects.filter(publish__name='东方出版社').values('title'))
# 反向查询:
print(models.Publish.objects.filter(name='东方出版社').values('book__title'))

# 5. 查询书籍id是2的作者的手机号
# 正向查询:
print(models.Book.objects.filter(pk=2).values('authors__author_detail__phone'))
# 反向查询:
print(models.Author.objects.filter(book__pk=2).values('author_detail__phone'))

聚合函数

aggregate

聚合函数 描述
Max 最大值
Min 最小值
Sum 求和
Count 计数
Avg 平均数
'''
    聚合函数
    '''
from django.db.models import Max,Min,Count,Avg,Sum

# 筛选出价格最高的书籍
print(models.Book.objects.aggregate(ct=Count('price'))) # {'ct': 2}

# 筛选出价格最低的书籍
print(models.Book.objects.aggregate(mn=Min('price')))   # {'mn': Decimal('222.00')}

# 求书籍价格总和
print(models.Book.objects.aggregate(sm=Sum('price')))   # {'sm': Decimal('666.00')}

# 求书籍价格平均值
print(models.Book.objects.aggregate(ag=Avg('price')))   # {'ag': 333.0}

# 联用
print(models.Book.objects.aggregate(Max('price')),Min('price'),Sum('price'),Count('price'))

分组查询

annotate

'''
    分组查询,通常和聚合函数一起使用
    '''
# 1. 统计每一本书的 书名和对应的作者个数
print(models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num'))

# 2. 统计每各出版社卖的最便宜的书的价格
print(models.Publish.objects.annotate(book_num=Min('book__price')).values('name','book_num'))

# 3. 按照指定的字段进行分组
print(models.Publish.objects.values('name').annotate(max_price=Max('book__price')).values('name','max_price'))

# 4. 统计不止一个作者的图书
# 首先拿到书对应的作者数,再筛选出大于一的图书,作者个数
print(models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num'))

# 5. 查询每个作者出的书的总价格
print(models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price'))

F与Q查询

'''
    F 查询
    '''
from django.db.models import F,Q

# 1. 查询库存数大于卖出数的书籍
print(models.Book.objects.filter(kucun_num__gt=F('maichu_num')).values('title'))

# 2. 将所有书的价格都上涨100块
models.Book.objects.all().update(price=F('price')+100)

# 3. 将所有书的名字后面都加上 后缀名
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'),Value('新款')))

'''
    Q 查询
    '''
# 1. 查询书籍名是三国演义或者 库存数是500的书籍
print(models.Book.objects.filter(title='三国演义',kucun_num=500))   # 默认是and的关系
print(models.Book.objects.filter(Q(title='三国演义') | Q(kucun_num=500)))   # 用了 | 就是or的关系
print(models.Book.objects.filter(~Q(title='三国演义') | Q(kucun_num=500)))   # 用了 | 就是or的关系

'''
    Q对象高级用法
'''
q = Q()
q.connector = 'or'  # 默认是and,可以改成or
q.children.append(('title','三国演义'))
q.children.append(('kucun_num',500))
print(models.Book.objects.filter(q))

ORM事务

还记得在Mysql数据库中的事务操作吗?

在MySQL中只有使用Innodb数据库引擎的数据库或表才支持使用事务

事务必须满足4个条件(ACID):

  • 原子性:一个事务中的所有操作,要么全部执行,要么全部回滚,中间不会结束在任何一个环节。
  • 一致性:在事务开始和事务结束之后,数据不会出错
  • 隔离性:数据库允许多个并发事务同时对数据进行读写和修改,每个事务相互隔离
  • 持久性:事务处理结束后,对数据的修改是永久的
from django.db import transaction

with transaction.atomic():
    # 在缩进的代码中书写数据库的操作
    # 该缩进下的所有代码,都是一个事务
    pass


# 对整个view视图开启事务
@transaction.atomic
def index(request):
    //ORM操作
    return ....

例子

1.创建一个项目,新建一个APP(基础操作,这里不再赘述)

2.通过ORM创建生成表

from django.db import models

class UserInfo(models.Model):
    username = models.CharField("用户",max_length=32)
    balance = models.CharField("余额",max_length=32)

注意啊:踩过的坑,涉及金融计算,涉及小数啊,要求特别精确的,我们用字符串存储。

如果是金融计算的话,我们用一个decimal来进行计算。

3.我们给数据库加两条数据,用来模拟两个用户之间的转账

4.配置URL

5.创建对应的视图函数

from django.shortcuts import render,HttpResponse
from app01 import models
from django.db import transaction
from django.db.models import F

def index(request):
    try:
        with transaction.atomic():
            models.UserInfo.object.filter(id=1).update(balance=F("balance")-100)
            models.UserInfo.object.filter(id=2).update(balance=F("balance")+100)
    except Exception as e:
        return HttpResponse("出现错误<%s>"%str(e))
    return HttpResponse("执行成功")

当我们访问index的时候,会进行一次转账操作

6.现在,我们让他报错

from django.shortcuts import render,HttpResponse
from app01 import models
from django.db import transaction
from django.db.models import F

def index(request):
    try:
        with transaction.atomic():
            models.UserInfo.object.filter(id=1).update(balance=F("balance")-100)
            raise 一个错误
            models.UserInfo.object.filter(id=2).update(balance=F("balance")+100)
    except Exception as e:
        return HttpResponse("出现错误<%s>"%str(e))
    return HttpResponse("执行成功")

我们再次查看数据库文件,如果没有数据的原子性操作,我们第一条sql执行完报错,那钱肯定是减去了

但是,我们进行的是原子性的操作,你会发现钱没有减诶。

完美,没毛病

数据库中的三大范式

范式(Normal Form),缩写NF,规范化形式,简称范式。

目的是增加数据有效性,减少数据冗余,提高存储效率

  • 1NF:数据库中的每一个字段,必须是不可拆分的最小单位。
  • 2NF:表中所有字段都必须有意义,一个表只描述某一个事物
  • 3NF:表中不能有其他表中存在的、存储相同信息的字段,通常是通过外键去建立关联,外键约束

第一范式(1NF)

数据库中的每一个字段,必须是不可拆分的最小单位。

即一个字段就表示一个意思,表中不能同时有2个字段来表示同一个意思

正例:

根据业务需求来合理使用行政区域

反例:

其中 address 可以再分为省、市、地区(县)、街道、详细地址,违反了第一范式。

第二范式(2NF)

表中所有字段都必须有意义,一个表只描述某一个事物

主键存在的意义就是唯一的标识表中的某一条记录,如果某一列和该行记录没关系,也就没必要存在。

反例:

此表中,天气(weather字段)和用户没啥关系,也就不存在依赖关系,所不符合 第二范式。正确的做法应)该删除此列,如有其他需要可单独存在一张表中。

第三范式(3NF)

表中不能有其他表中存在的、存储相同信息的字段,通常是通过外键去建立关联,外键约束

反例:

上面是一个订单表,字段从左至右以此是:订单id、买家id、买家名称、买家性别、买家年龄、订单状态。其中字段buyer_name、buyer_gender、buyer_age 是依赖于字段 buyer_info_id,违反 第二范式。

正例:

订单表

img

买家信息表

img

数据库五大约束

  • 主键约束(Primary Key):非空唯一,例如 将UserId作为主键
  • 唯一约束(Unique):唯一,可以空,但只能有一个,例如 身份证号唯一,因为每个人的都不一样
  • 检查约束(Check):对该列数据的范围、格式的限制(如:年龄、性别),例如 对年龄加以限定 20-40岁之间
  • 默认约束(Default):该数据的默认值,例如 如果地址不填 默认为“地址不详”
  • 外键约束(Foreign Key):需要建立两表间的关系,例如 建立外键
posted @ 2019-11-28 19:31  GeminiMp  阅读(293)  评论(0编辑  收藏  举报