V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
RedBeanIce
V2EX  ›  Java

[mysql 字段] not null 还是 null default

  •  1
     
  •   RedBeanIce · 2022-09-04 18:52:16 +08:00 · 5524 次点击
    这是一个创建于 845 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题:如图,字段值的设置

    今天在看一篇文章,设计数据库的时候所有字段都设置为 not null ,并且给默认值 default null/0 ,我感觉这是不符合实际场景的

    我回想我入门的时候也看到过这样子的文章,但是我工作了两三年后发现,我都是根据业务实际来做的,业务允许为空那就是 null ,业务不允许为空我就给 not null ,这个是否必填由产品说了算。

    至于 default 属性,是只有选择框,下拉框才需要做的事情。否则都没有 default

    ==========================隔断线=======

    null 还是 not null:根据产品要求来设计

    default:根据实际场景,一般只在下拉框等场景使用

    如上,请各位指教,我这样应该是对的吧,这类似的文章都是不符合实际的吗?

    53 条回复    2022-09-06 08:43:29 +08:00
    akira
        1
    akira  
       2022-09-04 19:05:39 +08:00   ❤️ 1
    看实际情况来,尽信书不如无书
    GTim
        2
    GTim  
       2022-09-04 19:58:02 +08:00   ❤️ 1
    这几年的经验来看,能 `not null` 的都 `not null`,默认值能不 `null` 的就不要 `null`
    fengpan567
        3
    fengpan567  
       2022-09-04 20:02:02 +08:00   ❤️ 1
    待过一家公司也是按这个规范来的,真的蠢。审核人员把我提交的 sql 脚本打回来,说里的所有字段必须有默认值并且不准为 null ,可我时间字段怎么给你默认值,这个时间是用户自己去填的,争论了几句还是让我改,说自己不管业务只管审核
    PendingOni
        4
    PendingOni  
       2022-09-04 20:05:15 +08:00   ❤️ 1
    主要看那些字段是不是出现在建立的索引上或者在视图上比较常用
    XiLingHost
        5
    XiLingHost  
       2022-09-04 20:05:33 +08:00   ❤️ 2
    @fengpan567 时间如果非要有个默认值,我觉得可以用`1970-01-01T00:00:00.000Z`
    GTim
        6
    GTim  
       2022-09-04 20:06:50 +08:00
    @fengpan567 做过国际化的基本都有一个共识:能用 bigint 来保存时间,都不要用 timestamp 或者 datetime ,不然后期可能是个超级大工程
    fengpan567
        7
    fengpan567  
       2022-09-04 20:11:57 +08:00
    @XiLingHost 我直接用字符串存时间了,反正已经跑路。。。
    xxbing
        8
    xxbing  
       2022-09-04 20:29:38 +08:00   ❤️ 1
    @GTim 不能同意更多. 后期需要什么格式 前端 format 就行了
    zpf124
        9
    zpf124  
       2022-09-04 21:12:35 +08:00   ❤️ 1
    @GTim 可以存入数据库的时间都使用 UTC 时间,程序读取和写入的时候做转换。
    zjp
        10
    zjp  
       2022-09-04 21:53:45 +08:00   ❤️ 1
    #2 一样的情况。要存的是金额,null 值、负数和零都是实际场景下存在的。那就只能用字符串了
    iseki
        11
    iseki  
       2022-09-04 21:59:28 +08:00   ❤️ 4
    @GTim 说的好像用 timestamp datetime 前端不用格式化似的。啊,你们不会都拿 String 传时间吧
    lolizeppelin
        12
    lolizeppelin  
       2022-09-04 22:55:23 +08:00   ❤️ 1
    @zpf124
    linux 时间就是 utc 的

    一般情况下字段都应该 not null,尽量用空字符串或者 0 来表达空,避免索引问题

    当字段有是外键或唯一索引的时候,字段只能用到 null 占位,这时候就得允许 null
    Oktfolio
        13
    Oktfolio  
       2022-09-04 22:57:21 +08:00   ❤️ 1
    @iseki 我们就是这么干的,返回给前端,前端传的都是 yyyy-MM-dd HH:mm:ss ,刚看到的时候我人都惊呆了
    ilylx2008
        14
    ilylx2008  
       2022-09-04 23:14:47 +08:00   ❤️ 1
    所有字段 not null+默认值
    RedBeanIce
        15
    RedBeanIce  
    OP
       2022-09-04 23:22:40 +08:00
    @ilylx2008

    但是我好奇为什么呢,如果不是下拉框这种规定死的,

    你一个数值字段,保存是没填的,回显返回一个 0 ,产品或者业务方无法接受你怎么办。
    oneisall8955
        16
    oneisall8955  
       2022-09-04 23:42:58 +08:00   ❤️ 1
    @ilylx2008 #14
    @GTim #6

    层遇到个业务场景,客户支付时间,MySQL 字段类型是 datetime ,对应 Java 是 java.util.Date ,如果 db 要 not null ,在程序上兼容?
    dobelee
        17
    dobelee  
       2022-09-04 23:46:05 +08:00   ❤️ 1
    避免空指针问题。
    RedBeanIce
        18
    RedBeanIce  
    OP
       2022-09-05 00:11:19 +08:00
    @dobelee 但是我好奇为什么呢,如果不是下拉框这种规定死的,

    你一个数值字段,保存是没填的,回显返回一个 0 ,产品或者业务方无法接受你怎么办。
    a132811
        19
    a132811  
       2022-09-05 00:15:29 +08:00   ❤️ 1
    @GTim 你们做国际化时,传时间都不带时区吗?我们都统一用 rfc3339 表示
    IvanLi127
        20
    IvanLi127  
       2022-09-05 00:45:35 +08:00 via Android   ❤️ 1
    @GTim 你们国际化和存啥类型的数据有啥关系?不应该考虑存的时间带不带时区么?
    dobelee
        21
    dobelee  
       2022-09-05 01:03:27 +08:00
    @RedBeanIce 你可以能搞错了,显示的问题应该由外层处理的。大多数情况可以用零值作为默认值的。
    难道你想通过判断空指针来检查是否保存?这样是不靠谱的,结果可能随着不同语言的特性而变化。
    phithon
        22
    phithon  
       2022-09-05 01:12:09 +08:00   ❤️ 1
    @GTim 国际化不用 RFC3339(ISO 8601)用时间戳?这么说 Django 不是国际化框架了
    iseki
        23
    iseki  
       2022-09-05 02:05:56 +08:00 via Android   ❤️ 1
    @Oktfolio 我这边的做法都是代码中只出现时间类型,Java 里用 Instant Go 里用 time.Time ,系统边界尽量用 RFC3339 字符串传递
    iseki
        24
    iseki  
       2022-09-05 02:09:14 +08:00 via Android
    我倾向于是否允许 null 跟着业务走,尽量不用零值做特殊值,null 就是 null ,0 就是 0
    kiwi95
        25
    kiwi95  
       2022-09-05 07:15:34 +08:00 via Android   ❤️ 1
    @RedBeanIce 一般都能接受。其实说白了都是为了自己方便,减少出问题的点,dba 不想要 null 值是为了防止 MySQL 莫名其妙的问题,研发为了 SQL 能过审核并且能少一些空指针判断 /避免指定义针类型也想自己方便就都 not null 。

    产品不同意就去说服他们。
    ksc010
        26
    ksc010  
       2022-09-05 08:04:45 +08:00   ❤️ 1
    国际化 用 ISO 啊
    GTim
        27
    GTim  
       2022-09-05 09:18:35 +08:00   ❤️ 1
    @iseki
    @zpf124
    @a132811

    带时区,前端要先 parse 一次,然后再 format 一次对吧。这种东西没有对错,如果一开始就是国际化的,怎么做都好说,一开始没有国际化的,又倾向于可能会有,我觉得 bigint 是合适的
    lolizeppelin
        28
    lolizeppelin  
       2022-09-05 10:12:26 +08:00   ❤️ 1
    @RedBeanIce
    你傻哦 遇到 0 的前端显示未设置不就行了...

    但是这个字段有唯一索引就必须 null 了,否则就设 0 前端对 0 特殊处理
    用 0 不用 null 是索引问题
    RedBeanIce
        29
    RedBeanIce  
    OP
       2022-09-05 10:30:02 +08:00
    @dobelee
    @lolizeppelin
    @ilylx2008
    那么问题来了,用户输入了一个 0 ,后端返回给前端,怎么知道是用户输入的 0 ,还是 mysql 默认值的 0
    用户说,我明明输入了。为啥没了。
    RedBeanIce
        30
    RedBeanIce  
    OP
       2022-09-05 10:30:34 +08:00
    @kiwi95 请看上一条
    meiyoumingzi6
        31
    meiyoumingzi6  
       2022-09-05 10:35:51 +08:00 via iPhone
    看到归类到 java

    我想站在其他语言角度来看,
    python 想搞这个事情也是 so easy ,
    golang 就略蛋疼了,如果是 null ,就不得不在创建 struct 的时候把字段设置成指针类型,害,要不他就是零值,即:字符串类型是空串,数字类型是 0
    morty0
        32
    morty0  
       2022-09-05 10:46:42 +08:00
    @RedBeanIce 用户输入需要避开默认值
    pastor
        33
    pastor  
       2022-09-05 12:24:22 +08:00
    @RedBeanIce #29

    对于用户是否应该必填字段,产品规则是可控的,即使不需要用户输入;
    默认值也是产品规则可控的,比如字符串类型,一般默认就"",用户如果可以不输入,""也没问题。数值类型 0 值同样道理。

    但是数据库 null 对于不同语言处理起来可能会遇到不同的麻烦。而对于默认值。

    不喜欢 not null default 的各位,建议还是反思下自己吧

    支持 not null default +1
    支持 bigint 时间戳 +1
    iseki
        34
    iseki  
       2022-09-05 12:46:21 +08:00 via Android
    @RedBeanIce 用 Unix 时间戳也不是不行,这个问题和时区也没什么关系,就是 RFC3339 是明确的标准,时间戳就稍差点。
    我的重点是不管用哪种,前端都需要处理,不是说用时间戳前端就省多少事
    iseki
        35
    iseki  
       2022-09-05 12:48:27 +08:00 via Android
    @pastor 使用零值就没有麻烦了吗🤣
    lovelylain
        36
    lovelylain  
       2022-09-05 13:12:13 +08:00 via Android
    @GTim db 用 datetime 存,db 访问模块转换为时间戳,国际化会有什么问题吗?
    RedBeanIce
        37
    RedBeanIce  
    OP
       2022-09-05 13:24:53 +08:00
    @pastor
    请问一下,一个非必填字段。如何判断用户输入 0 ,还是 default 0 ,,

    用户这个字段没有输入,回显的时候你给个 0 返回出去,不是扯淡了。。产品不知道是否可以接受这种处理方式。

    请指教
    lovelylain
        38
    lovelylain  
       2022-09-05 13:25:29 +08:00 via Android
    @iseki 可以去了解下 protobuf3 ,尽量把零值作为缺省的特殊值,例如零值表示未设置。很多地方都是可以避免零值作为业务值的。
    RedBeanIce
        39
    RedBeanIce  
    OP
       2022-09-05 13:25:58 +08:00
    @pastor 产品规则可控,感觉好麻烦。。。。
    sunmoon1983
        40
    sunmoon1983  
       2022-09-05 13:26:35 +08:00
    @fengpan567 那你修改了没有呀?我也碰到了这种情况!
    ilylx2008
        41
    ilylx2008  
       2022-09-05 13:37:16 +08:00   ❤️ 1
    @RedBeanIce 看产品定义,产品要显示 0 那你整形默认值改为比如-1 。
    具体情况具体分析。
    不想显示 0 那就前端改为空。
    pastor
        42
    pastor  
       2022-09-05 13:37:28 +08:00   ❤️ 1
    @RedBeanIce #37
    "请问一下,一个非必填字段。如何判断用户输入 0 ,还是 default 0 ,,"
    —— 你既然根据产品规则设置了默认是 0 ,也就是可以给用户端 0 用来展示,那不管是不是用户输入的 0 ,都是正确的作为前端展示的值。
    搞清楚一点,提高产品思维,这个前提下,实现业务逻辑的重点是为了产品合理性,而不是为了去满足你区分到底是不是用户输入的强迫症需求

    #39 "产品规则可控,感觉好麻烦。。。。"
    —— 我们程序员骂产品"狗"的时候,很多是因为产品不懂技术瓶颈压力、随便什么需求都敢提
    —— 而产品骂程序员的时候也很多是因为程序员只考虑技术、不考虑产品

    前阵子雷军不好举例子他亲自下场去当销售才体会到技术人员太执着于技术了做不好产品的嘛

    嫌麻烦,其实就是咱没经历过好厂好项目好规范的训练,作坊式的生产、不规范习惯了导致了懒惰,然后反倒去以为别人这么做是多余、说人家麻烦。。

    规范、严谨的工程,比如知名开源,或者硅谷那些知名厂的多数项目,规范化得多,提交、合并流程各种繁琐。说起来麻烦,但确实这很规范,大家都规范,也是对自己能力的提升
    pastor
        43
    pastor  
       2022-09-05 13:39:49 +08:00
    @iseki
    默认值不一定是 0 。
    结合产品需求的默认值设计,我#33 已经明确讲过了,#42 有更多补充。
    zpf124
        44
    zpf124  
       2022-09-05 13:40:31 +08:00
    @GTim 你说了半天我还是没懂,bigint 是如何存储时区信息的? 转 UTC 时间的 timestamp ?

    那么用 datetime 还是用 bigint 有差别吗? 为了防止人肉眼看的时候能直接看出来这个是什么时间吗?

    bigint 和 timestamp 最大的差距仅仅是 2038 的长度问题, 除此之外他们在存储本质上没区别,只是后一个 mysql 可以自动处理给你显示成人类可读的时间。
    xiangyuecn
        45
    xiangyuecn  
       2022-09-05 13:50:27 +08:00
    统统用 varchar ,默认值 '' ,益寿延年,🐶🐶🐶🐶
    zpf124
        46
    zpf124  
       2022-09-05 13:52:23 +08:00   ❤️ 1
    另外,这么多人聊了这么多 居然只有 @kiwi95 提到这个问题的重点,mysql 的 null 索引有性能问题,会慢非常多!

    要求 not null + default 的规则制定者中能说明白的大拿都会和你明确的说,mysql 非 null 查询性能会差一个数量级。

    这是 mysql 的问题,不知道 8.0 有没有解决,最起码 5.5 5.6 这些规则书写成那个年代的版本是真实存在的性能问题。
    而且这只是 mysql 的问题,mysql 小毛病一大堆,所以你网上看到的许多数据库优化的点实际上并不通用,就是针对 mysql 的。

    上学时候 学 sql server 还在天天教数据库三范式的年代,哪听说过这种要求。
    zpf124
        47
    zpf124  
       2022-09-05 13:58:29 +08:00
    最后,所有支持 存在 null 才是符合大多数 程序员逻辑的想法, 除非你入门编程语言恰好是非 C 系那几个排斥引用类型有 null 值的语言。

    因为数据不存在 null 和 数据存在值为 0 ,含义是不一致的。
    让他们表达相同的含义就和前端页面父子组件样式有问题导致挤压错位,然后用绝对定位或者 margin 负数移动回来一样。
    在外在表现上看起来是没问题了,但实际上从根本逻辑上讲他就不应该这么处理。
    optional
        48
    optional  
       2022-09-05 14:52:03 +08:00 via iPhone
    @GTim 做过国际化的表示,timestamp with time zone 方便多了,pg
    optional
        49
    optional  
       2022-09-05 14:55:30 +08:00 via iPhone
    默认值是业务控制的,不是数据库控制的。数据库尽量不用默认值,空与非空,除非必要,不然可空,业务不可空交给业务控制。
    kiwi95
        50
    kiwi95  
       2022-09-05 14:57:56 +08:00 via Android   ❤️ 1
    @RedBeanIce 好,现在给你这个场景做面试题,你真的没有方案吗?

    方案肯定有的,就看愿不愿意用。主要还是考虑统一和方便性。前后端通信有意义的值为什么一定要定义 0 呢,0 只是抽象的符号,用什么都可以。
    fournoas
        51
    fournoas  
       2022-09-05 14:58:02 +08:00
    @fengpan567 对。时间戳不允许 NULL 就是___🖊
    lolizeppelin
        52
    lolizeppelin  
       2022-09-05 15:05:51 +08:00   ❤️ 1
    @RedBeanIce
    所以说你傻呀....不让用户输 0 不就可以了
    难道你还要杠用户绕过 UI 设 0 ?
    vishun
        53
    vishun  
       2022-09-06 08:43:29 +08:00
    主要是 mysql 中如果设置 null 会有很多坑,例如在条件判定需要 is not null ,或者 sum 、groupby 这些函数结果,再有就是部分情况下索引失效,这些都是一不留神会出错的,就算你不出错,你也不能保证团队其他人不出错,所以如果能用空值或者是 0 标识且没有疑义的化还是不要用 null 来比较好。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2833 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 14:10 · PVG 22:10 · LAX 06:10 · JFK 09:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.