sql 表设计问题:需索引的可选字段应该允许 null 吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
1800x
V2EX    数据库

sql 表设计问题:需索引的可选字段应该允许 null 吗?

  •  
  •   1800x 2022-12-29 10:16:35 +08:00 4386 次点击
    这是一个创建于 1021 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如一个用户表,中间一个字段,表示用户所属的企业。

    用户可能不属于任何企业。

    那么有两种解决方案:

    1. 企业字段允许为 null 。用户不属于企业时,企业字段为 null 。
    2. 企业字段不允许为 null ,默认 0 。用户不属于企业时,企业字段为 0 。

    再叠加一个需求,用户表的企业字段需要加索引。

    那么,应该选择哪个方案?或者有没有更好的方案?

    42 条回复    2023-04-11 14:29:45 +08:00
    root000
        1
    root000  
       2022-12-29 10:20:40 +08:00
    2
    Xhack
        2
    Xhack  
       2022-12-29 10:22:37 +08:00
    is null And is not null 是不会走索引的
    isora
        3
    isora  
       2022-12-29 10:24:20 +08:00
    iseki
        4
    iseki  
       2022-12-29 10:25:54 +08:00 via Android
    依业务需求而定,数据库不能好好处理就说明该换数据库了
    Xhack
        5
    Xhack  
       2022-12-29 10:26:56 +08:00
    @Xhack 我说错了
    imv2er
        6
    imv2er  
       2022-12-29 10:33:19 +08:00
    业务逻辑设计上选 1
    有查询需求的 选 2
    securityCoding
        7
    securityCoding  
       2022-12-29 10:34:16 +08:00
    2
    lambdaq
        8
    lambdaq  
       2022-12-29 10:50:27 +08:00
    mysql 还是别用 null 了。
    CodeCodeStudy
        9
    CodeCodeStudy  
       2022-12-29 10:59:06 +08:00   8
    mysql 字段不使用 null 的理由

    1 、比如文章点击量加 1 ,column_name = column_name + 1 ,如果把字段设成 null ,并且插入数据时没指明 column_name = 0 ,那么+1 操作不起作用;
    2 、count(column_name)时,null 的列不包含在内,count(*)则包含 null 的列在里面
    3 、计算多列时,如 SELECT id, click1+click2 as click FROM `foo` 如果两个点击量有一个为 null ,那么相加结果就是 null
    4 、如果有比较条件,比如 where < 10 ,如果为 null 的话则不包含在内
    5 、min(column_name), max(column_name)如果字段有值,则用值比较,如果字段没有值,都是 null 的时候,则为 null

    总结:不能运算,不能比较,慢
    jarvanluo
        10
    jarvanluo  
       2022-12-29 11:06:10 +08:00
    mysql 我一般都是有默认值,不允许 null 的。text 除外。
    bthulu
        11
    bthulu  
       2022-12-29 11:11:24 +08:00
    我反正是这么来的, 所有表字段都必须设置不允许为 null.
    这样在 java 里会减少很多 NPE.
    aichunya
        12
    aichunya  
       2022-12-29 11:29:31 +08:00
    现在项目里面,除了类似'收货时间'这种具有业务意义的 date 类型的字段允许为 null 以外,其它类型字段都会设置默认值
    makelove
        13
    makelove  
       2022-12-29 11:41:47 +08:00   2
    别听楼上的瞎 bb ,这种情况明显用 null
    itechify
        14
    itechify  
    PRO
       2022-12-29 11:43:32 +08:00
    某些 datetime 含业务含义和 text 类型的允许 null ,其余都 not null 了
        15
    clockwork1122  
       2022-12-29 12:18:21 +08:00
    @oneisall8955 datetime 是真让我最讨厌的玩意,一般查询的场景都只需要 date 就行,数据库用 datetime 给自己增加查询难度。
    clockwork1122
    cpstar
        16
    cpstar  
       2022-12-29 12:37:34 +08:00
    @CodeCodeStudy 9# 你这些数值比较的字段,不应该把字段设置为数值类型的同时默认值是 0 ?

    oracle 的 varchar2 类型,空值即为 null 。
    meshell
        17
    meshell  
       2022-12-29 13:09:21 +08:00
    我说个其它的,在使用 ORM 的 one many 的时候 null 和 0 区别对待的。0 是找不到企业报错,null 没有企业
    yfwl
        18
    yfwl  
       2022-12-29 13:16:08 +08:00
    如果你要给这个列创建索引,那就不能 null ,因为 null 不走索引的,正常的列是允许 null 的
    SethShi
        19
    SethShi  
       2022-12-29 13:23:28 +08:00   4
    楼上的是不是都要更新知识了? MySQL null 列可以走索引.
    我的建议还是你用 null, 毕竟 null 和 0 还是不一样的.
    至于加不加索引和 null 不 null 无关, 取决于你的用户是否大多数有企业.
    比如你一百万用户就一百个有企业, 别加了. 如果大多数都有, 那就加
    SethShi
        20
    SethShi  
       2022-12-29 13:24:55 +08:00
    再补充一个, 取决于你的业务需求, 如果你业务代码都是.
    1. 先去查找用户 -> 然后在通过用户的企业主键去查找企业 = 不用加索引
    2. 找用户 -> join 企业 = 加索引
    3. 找这个企业下的用户 = 加索引
    Konys
        21
    Konys  
       2022-12-29 13:26:04 +08:00
    null 是可以走索引的
    cokar
        22
    cokar  
       2022-12-29 13:28:5 +08:00
    使用空字符串不是更好吗?
    zhangtest
        23
    zhangtest  
       2022-12-29 14:26:04 +08:00
    @Xhack 大哥们,你们了解下 mysql 不同版本的区别吧,别总把古董版本的特性带到目前常用的版本。
    BQsummer
        24
    BQsummer  
       2022-12-29 15:48:10 +08:00
    有些业务场景 0 和 null 就是有区别的, 咋办
    CodeCodeStudy
        25
    CodeCodeStudy  
       2022-12-29 16:28:13 +08:00
    @cpstar #16 是啊,应该设为设置型,默认值为 0 ,用 0 来表示特殊的含义,而不是 null
    Soler
        26
    Soler  
       2022-12-29 16:47:12 +08:00   1
    null 是 null, 0 是 0 ,概念首先不要乱用!!!

    假设设置成 0 , 0 多了不一样走不了索引吗。
    IvanLi127
        27
    IvanLi127  
       2022-12-29 17:24:00 +08:00   1
    @CodeCodeStudy 其实这个不好,我觉得更好的方案是:

    创建一个缺省公司,用来给那些不属于任何企业的用户挂名的;
    然后直接不允许这个字段为 NULL 就好了。

    这个方案就是前几年讨论 NULL 有没有必要时,一种比较优雅的方案。

    不过要是我的话,如果业务不要求对这类用户做什么操作,我肯定直接设 NULL 了 哈哈
    CodeCodeStudy
        28
    CodeCodeStudy  
       2022-12-29 17:33:08 +08:00
    @IvanLi127 #27 不用创建缺省公司来挂名,这样反而会搞复杂,企业字段直接用 0 来表示就好了
    IvanLi127
        29
    IvanLi127  
       2022-12-29 17:42:10 +08:00
    @CodeCodeStudy 其实有个问题。。。你们用 0 的前提是不用外键约束吗?如果用的话应该势必创一个缺省公司
    JKeita
        30
    JKeita  
       2022-12-29 17:43:51 +08:00
    基本都不允许 null ,能不 null 就不 null
    CodeCodeStudy
        31
    CodeCodeStudy  
       2022-12-29 17:43:59 +08:00   1
    用 0 或者-1 来表示该值不存在更好的理由是保持一致的数据类型,避免 null 造成 NPE ,比如 Java 和 Javascript 的字符串的 indexOf 的返回值是 int ,用-1 来表示找不到,而不是返回 null
    CodeCodeStudy
        32
    CodeCodeStudy  
       2022-12-29 17:44:31 +08:00
    @IvanLi127 #29 不需要外键
    CodeCodeStudy
        33
    CodeCodeStudy  
       2022-12-29 17:52:26 +08:00
    @CodeCodeStudy #9 补充一下第 4 点,如果某列允许为 null 的话,那么语句 where column_name > 10 ,where column_name < 10 ,where column_name = 10 均不能把实际值为 null 的行过滤掉,因为要用 where column_name is null ,这样就会带来不必要的麻烦
    xuanbg
        34
    xuanbg  
       2022-12-29 18:06:16 +08:00
    @seth19960929 也许他们的版本还停留在 5.5 或更早吧。。。反正我知道的 5.6 版本就已经支持 is null 走索引了。
    r4aAi04Uk2gYWU89
        35
    r4aAi04Uk2gYWU89  
       2022-12-29 18:39:45 +08:00   2
    按逻辑来说,null 是 null ,0 是 0 ,最好是按语意来使用。
    至于 select ... where x<y ,本来就不该带上 null 的数据。
    NoKey
        36
    NoKey  
       2022-12-29 19:00:43 +08:00
    如果公司都是用数字的话,就给一个默认公司的数字例如 0 ;如果是文字的话,就创建一个默认公司名,如:NoCompany 之类的,这样也不用纠结你这个问题了,查询起来也简单
    wiix
        37
    wiix  
       2022-12-29 19:08:59 +08:00
    null 是 null ,0 是 0 ,两者的语义完全不同,该是什么就是什么。
    把 null 用 0 表示一是使数据缺少一种表达能力,二是实际并没有减少做判断的心智负担。
    很多人讨厌 null 是因为 null pointer exception ,但有错却不能 fail fast ,带病运行更可怕。
    Outshine
        38
    Outshine  
       2022-12-29 21:30:06 +08:00   1
    @CodeCodeStudy #9 你这些理由对于遵循数据库设计范式的来说,完全不是问题。
    比如你说的文章点击量、需要比较大小或者参与计算的列,我找不到为 null 的场景。

    另外,mysql 早就支持 null 走索引了,知识要及时更新啊。
    iseki
        39
    iseki  
       2022-12-29 22:45:57 +08:00   1
    不要用 0 来表达没有,更不要自己搞出个 -1 来,这是在挖大坑;
    数据库该用 NULL 就用 NULL ,数据库不好处理,就换数据库;
    tairan2006
        40
    tairan2006  
       2022-12-30 08:54:15 +08:00
    如果大部分用户都有企业,那用啥都可以。
    如果大部分用户都没有企业,可以考虑搞个关联表,数据比较少。
    co2fe
        41
    co2fe  
       2022-12-30 09:04:59 +08:00   1
    为什么企业 id 要放在用户表里面,为什么不用关系表存储?
    万一你业务以后允许一个用户待在多个企业,你怎么搞。
    而且使用中间表,我觉得可以解决你这个纠结 null 还是 0 的问题。
    wi666
        42
    wi666  
       2023-04-11 14:29:45 +08:00
    选 1 ,并将企业名称字段加索引
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2985 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 12:39 PVG 20:39 LAX 05:39 JFK 08:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86