文档章节

性别字段存储时应该使用的字符串,还是数字?

翟志军
 翟志军
发布于 2016/03/30 09:32
字数 2055
阅读 1632
收藏 7
点赞 5
评论 11

在我和同事结对时,发现数据库中多个表中,分别都会有gender这个字段。比如A表,B表,C表。这三个表中,gender字段都是int类型。但是同一性别,在各个表中的值是不同的。比如A表中,1代表男,在B表中却代表了女,在C表中代表未知。

我突然意识到这背后存在更大的问题。从而引发我对“性别字段存储时应该使用的字符串,还是数字?”这个问题思考。也许已经有前辈思考过这个问题并写在某本书的某页,如果有,请告知。谢谢。

 

0代表女,1代表男

首先,你可能会问,对于这样的问题还用想吗?不是都使用数字吗?0代表女,1代表男。

其实,淘宝就是这么做的:

html代码是这样的:

 

这时,我会问如果这个用户没有填写性别信息呢?那你可能将原来的实现改成0代表空,1代表男,2代表女。我提醒你,当你开发的是一个大型网站时,你要将原来的“0代表女”改成“0代表空”,不会那么容易。历史数据要处理。你还需要修改所有用到0,1的代码,即使你使用的是常量代替而不是魔法数字,也不会容易到哪里去。

 

有经验的程序员

是的,有经验的程序员写代码时,一开始就会想到这个问题,所以一开始就设计“0代表空,1代表男,2代表女”。从前端到后端都统一使用数字。比如:

class User{
   final static int GENDER_NULL = 0
   final static int GENDER_MALE = 1
   final static int GENDER_FEMALE = 2
   int gender
}
class UserController{
    void saveUser(int gender)
}
<div>gender: #if($user.gender == 1)男#elseif($user.gender == 2)女 blabla….</div>

当然前端这样写有些难看,那我们使用宏来代替,比如<div>#displayGender($user.gender)</div>。这里我想留一个疑问:如果想国际化呢?你的displayGender怎么实现的?

实习生来了

某天公司招来了一个实习生要实现一个活动申请表页面。领导觉得这个功能应该不难,所以就将这个任务分配给他。他为了表现自己,哐啷哐啷很快就写完了,还得到了领导的表扬。但实习生根本没有参照前面有经验的程序员的写法(有时不是他的问题,可能是没有人告诉他需要参照某个功能的写法来实现)有意识一些的实习生还知道将gender的值写成常量,没有意识的,可能你只有去到前端页面看源码才能知道0, 1分别代表什么。

class ActivityApply{
     final static int GENDER_MALE = 0
     final static int GENDER_FEMALE = 1
     int gender
}

如果他没有参照前面有经验程序员的写法,我不确定他是否会重用那个前端宏。所以,讲到这里,你应该明白,有时你设计好的“重用”,并不一定会被重用。为什么呢?:P

这里不是故意贬低所有实习生,只是情节需要。

 

0和1到底放在哪里?

也许你意识到了(通常不包括架构师),我们需要统一将gender常量的值放在某个地方。那位有经验的程序员将其放到了User类中。这样,所有使用的gender的地方都应该变成User.GENDER_MALE blabla,如 activityApply.gender = User.GENDER_MALE。

也许有人想到了,建立一个Gender的类,或者枚举不就行了。比如:

public enum Gender {
    UNKNOWN(0), MALE(1), FEMALE(2);
    private final int value;
    Gender(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}

然后使用的时候就变成了:

user.gender = Gender.UNKNOWN
activityApply.gender = Gender.MALE

问题是不是解决了?就算是实习生来了,也能保证大家的gender的值是一致的。前提是他要知道关于gender的值我们取的都Gender枚举里的值。不论是入职时老员工跟他说,还是他自己发现的。

问题解决了?并没有。当前端发来了个gender参数时,我们如何校验这个参数呢?比如前文提到的淘宝表单里,我们看到:

_fm._0.g就是gender参数。

校验时,我们的controller里,有人可能会写成:

if(gender == 2){
     user.gender = Gender.FEMALE
}else if(gender == 1){
     user.gender = Gender.MALE
}else {
     user.gender = Gender.UNKNOWN
}

高明一些人的会在gender枚举中加入一个静态方法: 

public static Gender genderOf(int aGenderValue){
        for (Gender gender : Gender.values()) {
            if (gender.value == aGenderValue) {
                return gender;
            }
        }
        return Gender.UNKNOWN;
    }

然后校验时,

user.gender = Gender.genderOf(genderParameter).value

 

 

Gender以数字值存到数据库中,真是最好的方法?

以上,我们的思路看似没有问题。只是,我们没有看到其中的假设。以上思路的假设是:

  1. 代码使用者知道有Gender这个枚举类,然后再使用Gender枚举赋值给User.gender字段。

  2. 对于数据库中的0、1、2,只有我们的程序进行解释,其它程序里可能使用的是10、11、12

  3. 因为是性别可能的值不多,所以,前端代码写成if elseif elseif else,没所谓。但要知道,我们服务不仅仅输出html,还会输出json等其它格式。当然,你可以将这部分逻辑封装起来,这样别人就可以重用了。但是你这里又假设了“TA人知道你的重用”存在,然后正确使用。

首先,你可能认为这些不是问题。我猜想你给出的理由是:

关于1,一进入公司,我们就培训他,gender就使用这个类。或者写一个开发文档。

关于2,我们不需要其它程序解释这个数据库里的值,其他程序都是通过调用我们程序的。

关于3,性别来来回回就那几个,不会扩展到哪里去。

 

问题不在于Gender而在于别处

我觉得你这些理由是有道理的,但是不是最好的。

关于1:我们的代码应该设计得尽量可靠,可靠到连代码使用者都不会使用错。而User、ActivityApply的gender是int和String这类基础数据类型,不管你培训还是写开发文档,都会给代码使用者有写错的机会。更好的办法是什么,使用枚举类型。这样就可以由编译器来给我们检查代码使用者有没有调用错了。而且,这也解放了代码使用者的大脑。当你在写user.setGender(value)时,如果setGender接收的是一个枚举,IDE自然会提示你,gender有哪些值。

这很像地铁上的门写着“请勿倚靠”,你就认为真没有人倚靠?在合理成本内,地铁门应该设计成就算10个200斤的人倚靠都不怕。

这时,你会提问,如果User gender使用的枚举,那么我们怎么持久化到数据库中呢?如果使用ORM框架,它会给你解决。如果不使用ORM,也可以有技巧解决的。这个问题留你自己思考。但是,有一点需要提下,你的业务代码不应该和数据库这样具体技术耦合!可以看看我写的《耦合的本质》

关于2:如果养成了所有有限值内的字段都使用数字来存到数据库中的习惯,问题就没有使用gender这么简单了。在未来的几年,你的代码会充满魔法数字,最终架构腐化。

关于3:和2是同一个问题。

 

小结

回答本文标题的问题:出现像gender这样有限值的字段,我会优先使用枚举包装起来,持久化时,我会优先使用人看得懂的字符串。

深度一些的思考:

  1. 本文的标题是个问题吗?

  2. 设计模式能解决本文标题的问题吗?呵呵

  3. 为什么人们趋向于使用数字而不是字符串?

  4. 为什么架构会腐化?

  5. 架构师不写代码?

 

如果觉得这文章对你有帮助,可以赞赏10元:

© 著作权归作者所有

共有 人打赏支持
翟志军

翟志军

粉丝 339
博文 76
码字总数 79851
作品 2
深圳
程序员
加载中

评论(11)

l
lzszone
为什么会影响检索效率? 我认为会影响建索引的时间...检索效率几乎不影响
最后的夏天
最后的夏天
>>在合理成本内,地铁门应该设计成就算10个200斤的人倚靠都不怕

不让倚靠是怕夹住或者门故障突然开了掉下去吧不是怕人把门挤坏了吧 ,哈哈跑个题
啦啦啦拉拉
啦啦啦拉拉
根据国家标准 GB2261 性别选项有如下六个数字

0 - 未知的性别
1 - 男性
2 - 女性
5 - 女性改(变)为男性
6 - 男性改(变)为女性
9 - 未说明的性别
秋水逍遥
秋水逍遥
这里的重点是:建模的时候,“性别”是一个领域概念,值得用一个单独的构造(类/接口/枚举)来表示。只有在持久化这样的细节中,才需要考虑它的存储形式是数字、字符串还是其它。从大多数人直接用数字或字符串来表示性别来看,我看出两个错误的倾向:一个是“以数据为中心的设计”,另一个是“基本类型偏执”。
翟志军
翟志军

引用来自“chencliff”的评论

我就习惯有限值内的字段都使用数字来存到数据库中。原本数据库脱离了我的程序就是一堆谁也用不了的无意义的数据,不脱离我的程序,你很容易就能知道1/2/3/4都是什么
不要忘记了。除了你的程序去读这些数据,还可能有其他程序来读。第二,同一程序的不同版本也会读这些数据。
chencliff
chencliff
我大部分都会写成enum,还有一部分会写成public static final int,用起来很方便
chencliff
chencliff
我就习惯有限值内的字段都使用数字来存到数据库中。原本数据库脱离了我的程序就是一堆谁也用不了的无意义的数据,不脱离我的程序,你很容易就能知道1/2/3/4都是什么
Sonnet
Sonnet
以前处理这个的时候,都是1和0,默认就要选一个,不能为空,是不是有点粗暴?
叫我刀刀
叫我刀刀

引用来自“莫铭”的评论

引用来自“叫我刀刀”的评论

直接写汉字不行吗?

如果从数据库方面考虑,我不会使用字符。一个是占用空间问题,一个是检索效率问题。
有道理
莫铭
莫铭

引用来自“叫我刀刀”的评论

直接写汉字不行吗?

如果从数据库方面考虑,我不会使用字符。一个是占用空间问题,一个是检索效率问题。
MariaDB基础(1)--数据类型

MariaDB介绍 MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方...

xjxiaolei
2015/10/25
0
0
MySQL 高性能表设计规范

原文出处:高广超 良好的逻辑设计和物理设计是高性能的基石, 应该根据系统将要执行的查询语句来设计schema, 这往往需要权衡各种因素。 一、选择优化的数据类型 MySQL支持的数据类型非常多,...

高广超
2017/07/27
0
0
Mysql支持的数据类型(总结)

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://mrxiong.blog.51cto.com/287318/1651098 一.数值类型 Mysql支持所有标准S...

lingfeng72
2016/03/29
26
0
Mysql支持的数据类型(总结)

一.数值类型 Mysql支持所有标准SQL中的数值类型,其中包括严格数据类型(INTEGER,SMALLINT,DECIMAL,NUMBERIC),以及近似数值数据类型(FLOAT,REAL,DOUBLE PRESISION),并在此基础上进行扩展。 ...

chenhongjiang
2016/03/11
12
0
elasticsearch mapping

es的mapping设置很关键,mapping设置不到位可能导致索引重建。如何更好的设置mapping? 请看下面各个类型介绍^_^ core types 每一个JSON字段可以被映射到一个特定的核心类型。JSON本身已经为...

allantaylor81
2015/08/12
0
0
如何在mysql中实现自然排序

背景 熟悉mysql的同学应该清楚,mysql在对字符串做order by排序时是按照字典序进行排序的,但是如果字符串中包含数字的话(我们称这种类型的字符串为alphanumeric),仅按照字典序的排序结果...

wooyoo
2017/04/16
0
0
如何在mysql中实现自然排序

背景 熟悉mysql的同学应该清楚,mysql在对字符串做order by排序时是按照字典序进行排序的,但是如果字符串中包含数字的话(我们称这种类型的字符串为alphanumeric),仅按照字典序的排序结果...

wooyoo
2017/04/16
0
0
MySQL查询优化——使用索引和SQL优化

如何提高MySQL数据库的查询效率,可以从两个方面入手:使用索引和使用JOIN,本文主要讲使用索引的一些原则和优化方法。以及如何设计数据库和SQL语句,来避免一些会导致性能差的操作。 关于索...

_Zy
05/28
0
0
建表规约【参照阿里巴巴Java开发手册】

【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint ( 1 表示是,0 表示否)。 2. 【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,...

anlve
06/16
0
0
如何选择使用字符串还是数字呢?

在我多年的开发经验中,经常发现的一个情况就是,很多项目的对象字段或者是数据库字段本来是数字类型的,却被定义成字符串类型,这无关痛痒吗? 对于小项目来说,可能没什么影响,反正只要业...

杨尚川
2015/08/31
5.4K
31

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Win10专业版安装GIT后使用Git Bash闪退解决办法

百度后把过程和最终解决办法记录下来: 百度首先出来的解决办法如下: 来自:https://segmentfault.com/q/1010000012722511?sort=created 重启电脑 重新安装 安装到C盘 尝试网上的教程 \Git...

特拉仔
12分钟前
0
0
设计模式

1.装饰器模式 概念 允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。 实现 增加一个修饰类包裹原来的...

EasyProgramming
26分钟前
1
0
用python2和opencv进行人脸识别

一、安装cv2 sudo apt-get install python-opencv opencv-data 二、 Haar特征分类器 Haar特征分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值。包括人脸、眼睛、嘴唇等等。 ...

wangxuwei
26分钟前
0
0
python模板中循环字典

{% for k,v in user.items %} {{ k}} {{ v}} {% endfor %}

南桥北木
55分钟前
0
0
Java8系列之重新认识HashMap

简介 Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMap,类继承关系如下图所示: 下面针对各个实现类...

HOT_POT
59分钟前
0
0
获取调用方的className

/** * 获取调用方的class * @return */private static String getInvoke() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); S......

iborder
今天
0
0
深入了解一下Redis的内存模型!

一前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时,会接触Redis的5种对象类型(字符...

Java填坑之路
今天
1
0
从实践出发:微服务布道师告诉你Spring Cloud与Spring Boot他如何选择

背景 随着公司业务量的飞速发展,平台面临的挑战已经远远大于业务,需求量不断增加,技术人员数量增加,面临的复杂度也大大增加。在这个背景下,平台的技术架构也完成了从传统的单体应用到微...

老道士
今天
1
0
大数据学习的各个阶段

第一阶段:Linux课程讲解Linux基础操作,讲的是在命令行下进行文件系统的操作,这是Hadoop学习的基础,后面的所有视频都是基于linux操作的。鉴于很多学员没有linux基础,特增加该内容,保证零linux...

董黎明
今天
0
0
CVE-2013-0077 堆溢出分析

找了很久才发现这个环境比较容易搭建分析... 环境: 系统---Win XP SP3 漏洞程序:QQPlayer 3.7.892.400 出错DLL:quartz.dll 6.5.2600.5512 调试工具:x32db+gflag.exe 过程: 首先gflag设置...

Explorer0
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部