由来

  周末一人在家,叫了一份外卖,作为码农的我就对于外卖中日期的设定是怎么实现的提起了兴趣,于是就根据自己的想法记录一下如果我来实现,我要怎么做。
  场景是这个样子的:在外卖系统中有两种角色,一种是商家,一种是顾客,每一个商家可以设置自己一年中每个月中的哪些天可以提供外卖服务,哪些天不提供,顾客可以再任何一天打开订外卖的APP查看当天或者任意一天(假设可以)提供服务的商家。假设所有的数据都存储在数据库中,通过一个统一的webServer提供服务。这里不考虑其他因素。

需要实现的接口如下:
1、商家设置某些日期可以提供服务(传入一批日期,表示这些日期提供服务)
2、商家查看自己设定的可以提供服务的日期(传入一个年份+月份,查看提供服务的日期)
3、顾客查看某一个指定日期可以提供服务的商家(传入一个日期)

方案

1、方案1:
表设计:

列名 类型 说明
seller_id bigint 商家id,主键
year smallint 年份,主键
month tinyint 月份(1-12),主键
day tinyint 日期(1-31),主键

这种方案将每一天都作为一行存储,这样对于以上三种接口的处理如下:
1、商家设置某些日期提供服务(参数为id,提供服务日期的List)
对于每一个提供服务的日期执行一条sql:replace into table values(id, year, month, day);
2、商家查看某一个月可以提供服务的日期(参数为id,年份 Y 和月份 M)
执行sql:select day from table where seller_id = seller_id and year = Y and month = M;
然后根据查出来的day数组和输入的年和月转换成一个日期(年月日)返回。
3、顾客A查看一个日期可以提供服务的商家(参数为一个日期,年Y,月M和日 D)
执行sql:select distinct seller_id from table where year = Y and month = M and day = D;
然后再根据这些seller_id查询出详细的商家信息返回。

  这种方案实现比较简单,表结构比较简单,缺点是对于接口1,每一次要更新多条记录(一个日期一条),并且表中存在大量的冗余字段,如果调用该接口的时候一次传入一个月中所有可以提供服务的日期,那么就可以将seller_id、year、month提取出来。

2、方案2:
表设计:

列名 类型 说明
seller_id bigint 商家id,主键
year smallint 年份,主键
month tinyint 月份(1-12),主键
day1 tinyint 第一天,0表示不提供服务,1表示提供
day2 tinyint 第二天

这种方法按照每一个月存储一行,数据表一共34列,后面31列分别表示每一个月的1号到31号是否可以提供服务,可以则置为1,不可以则置为0.
1、商家设置某些日期提供服务(参数为id,提供服务日期的List)
首先根据这个商家每一个月份中提供服务的天保存在一个数组中,然后遍历数组根据数组的下标得到要插入的列名(day+下标)然后生成一个replace into table values ();
2、商家查看某一个月可以提供服务的日期(参数为id,年份 Y 和月份 M)
执行sql:select day1, day2, day3… from table where seller_id = seller_id and year = Y and month = M;
得到每一天的返回值(0或者1),然后再判断这些返回值中哪些是1,哪些是0,返回所有为1的日期。
3、顾客A查看一个日期可以提供服务的商家(参数为一个日期,年Y,月M和日 D)
执行sql:select distinct seller_id from table where year = Y and month = M and day+D = 1;
然后再根据这些seller_id查询出详细的商家信息返回。

  这种方案的优点是查询效率较高,缺点是实现太过于死板,扩展性较差,sql需要在代码中动态生成,无法固化。

3、方案3:
表设计:

列名 类型 说明
seller_id bigint 商家id,主键
year smallint 年份,主键
month tinyint 月份(1-12),主键
days varchar(128) 日期集合

这种方案按照每一个月存储一行,days列是这一个月中可以提供服务日期的序列化字符串,直接保存改天的数字,按照”:”进行分割。
1、商家设置某些日期提供服务(参数为id,提供服务日期的List)
首先根据这个商家每一个月份中提供服务的天保存在一个数组中,然后将数组序列化成一个字符串DS,假设提供服务的天为1,3,6,9,21,那么序列化后的字符串为”:1:3:6:9:21:”,每一个日期前面和后面添加一个分割符,然后执行sql:replace into table values(seller_id, Y, M, DS);
2、商家查看某一个月可以提供服务的日期(参数为id,年份 Y 和月份 M)
执行sql:select days from table where seller_id = seller_id and year = Y and month = M;
得到的是一个字符串,然后根据这个字符串反序列化为数组,最后的一个分隔符忽略。根据数组中的天,构造成日期返回。
3、顾客A查看一个日期可以提供服务的商家(参数为一个日期,年Y,月M和日 D)
执行sql:select distinct seller_id from table where year = Y and month = M and days like “%:D:%”;
因为在days序列化成字符串时每一个值的前面和后面都跟着一个分隔符,这样通过D加上前后的分割符就能够匹配到所有包含这一天的所有行。

  该方案的优点是查询比较固定,上层逻辑不用关心如何生成sql,缺点是接口3查询的时候需要使用like去匹配,性能可能会有所欠缺。

4、方案4:
表设计:

列名 类型 说明
seller_id bigint 商家id,主键
year smallint 年份,主键
month tinyint 月份(1-12),主键
days bigint 日期集合

这种方案的思想和方案三相似,但是使用了一个bigint(64位)存储所有的日,这是因为日的取值范围总是大于0小于32的整数,要表示一个或者多个这种数就可以使用每一位表示一个数,该位置为1表示这个数是由有效的,置为0表示该位是无效的,例如天数为(1,5,12,16,30)那么就可以表示为00100000000000001000100000010001(32位),之所以设置为bigint(8字节,64位是为了考虑扩展性)。
1、商家设置某些日期提供服务(参数为id,提供服务日期的List)
首先根据这个商家每一个月份中提供服务的天保存在一个数组中,设为List<Integer> days,执行如下代码:

long values = 0;
for(Integer day : days) {values |= 1 << day;
}

计算得到values之后执行sql:replace into table values (seller_id, Y, M, values);
2、商家查看某一个月可以提供服务的日期(参数为id,年份 Y 和月份 M)
执行sql:select days from table where seller_id = seller_id and year = Y and month = M;
得到的是一个long值values,执行如下代码得到所有的日,构造成日期字符串返回。

List<Integer> days = new LinkedList<Integer>();
for(int i = 1 ; i < 32 ; ++ i) {if(values & (1 << i) != 0 )days.add( i);
}

3、顾客A查看一个日期可以提供服务的商家(参数为一个日期,年Y,月M和日 D)
执行sql:select distinct seller_id from table where year = Y and month = M and days ^ 1 << D != 0;
直接使用SQL的位运算符就可以完成查询,将返回的商家信息返回。

  该方案的缺点是需要位运算,比较难理解,优点是简单高效。

扩展

  好了,以上四种方案能够满足商家设定日期和客户查看某一特定日期提供服务 的商家,但是一般情况下商家不一定是一整天都提供服务的,一般是几点到几点,如果一个商家每天的服务时间段是固定的,那么这个信息就可以保存在商家的信息中,但是假设商家可以每一天根据自己的心情来决定我今天几点到几点工作,这个时间精确到小时,以上的方案还能够满足新的需求吗?都应该如何扩展呢?

如果是第一种方案,那么就需要再添加一列表示小时,保存该商家提供服务的小时(0—24中的一个值),这会进一步的导致数据的冗余。
如果是第二种方案,那么就需要重新设计表,去掉所有的dayx字符,只保留一个day字段保存天,然后在创建24个hour字段保存该小时是否提供服务。
第三种和第四种方案的思想是相似的,一个序列化成字符串一个使用整数保存,都需要添加day字段,然后再添加hours字段保存工作的小时信息。但是第三种方案查询时使用LIKE不如使用位运算效率高。

  经过简单的对比,对于这种日期、小时之类的值的保存,个人比较推崇使用第四种方案,既不需要保存冗余的数据,也很容易构造出sql,而且使用的是高效的位运算,性能也不至于有所损失。

如果有更好的方法,请不吝赐教。

意淫设定时间系统de实现—由订外卖想到的相关推荐

  1. linux进程源码分析,Linux内核源代码分析——口述程序猿如何意淫进程(一)

    Jack:hi,淫龙,有空吗?我们来讨论一下Linux的进程吧. 我:没空.不要烦我,最近正在郁闷. Jack:郁闷啥呀? 我:最近大学城通了轻轨,房价涨得厉害,骂了隔壁. Jack:不要郁闷了,来研 ...

  2. 关于软件开发的随想,纯属意淫

    工作三年有余,抛开基础的编码能力,我的概念中的coder的几个阶段,纯属意淫 第一阶段 只想着开发,拿到需求,迫不及待,从满足眼前的需求都困难,到可以轻松应对开发需求[面向过程] 面对需求变更,几乎每 ...

  3. Linux内核之时间系统

    Linux内核之时间系统 1.Linux时间系统 (1)CMOS时钟 (2)系统时钟 (3)节拍数(jiffies) (4)墙上时间(xtime) 2.重要数据结构 (1)struct tk_read ...

  4. 安卓11客制需求:在设定时间开启深色主题模式,21点开启,次日8点关闭

    控制深色模式开启方式的代码在这个路径下面:frameworks/base/core/res/res/values/config.xml 这里可以看到注释里有三个值可以选,系统默认选择的是1,也就是不开 ...

  5. 【MATLAB航空航天工具箱】学习笔记--时间系统

    由于各种输入信息的时间系统并不一致,因此需要在统一的时间下进行各参数的转换. 格里高利历(MJD和GPS周/日) 国际原子时Temps Atomique International(TAI) 作用:协 ...

  6. 常用的时间系统有哪些?

    时间是基本物理单位之一,是信息时代的重要组成部分,是所有的物理量和物理常数中测量最为精确的物理量.时间可以分为"时刻"."时间段",用于描述一件事情发生的时间点 ...

  7. 请TMD别再意淫乔布斯了!

    乔布斯去世之后,乔布斯传大热,无论是怀揣朝拜之心的果粉乔迷.抱着学习心态的甲乙丙丁.以装逼为目的的二货莽夫,都开始读乔布斯传.评乔布斯传,读乔布斯.评乔布斯.但颇具讽刺意味的是,一方面我们接受着乔帮主 ...

  8. Unity 农场 1 —— 环境搭建、背包系统、时间系统

    目录 搭建初始地图环境 素材预处理 遮挡层级效果 景观的半遮挡与透明 人物移动 绘制瓦片地图 碰撞层 添加摄像机的边界 (Editor)使用 UI Toolkit 和 UI Builder 制作物品编 ...

  9. GPS研究---GPS 坐标系统和时间系统

    1.坐标系统 在 GPS 测量与应用中,通常采用的坐标系统有两大类.一类是地球坐标系, 该类坐标系是固结在地球上的,随地球一起转动,又称为地固坐标系(CTS). 第二类是天球坐标系,该类坐标系与地球自 ...

最新文章

  1. Javascript使用三大家族和事件来DIY动画效果相关笔记(一)
  2. 进一步考察与UI相关的安全漏洞-上
  3. 用C#写MSN机器人
  4. java jar metainf_java – 从生成的jar文件中排除META-INF / maven文件夹
  5. python编程(关于cocos2d)
  6. 句句真研—每日长难句打卡Day4
  7. Script:Diagnostic Resource Manager
  8. 基于进程的游戏Server端架构设计
  9. 问题及解决 —— 浏览器问题
  10. V4L2视频应用程序编程架构
  11. diagrams软件 可替换visio ProcessOn 亿图图示
  12. Java 接口实例化
  13. php smarty 翻译标签,Smarty自定义block标签
  14. lex 词法分析 linux,Lex词法分析器
  15. Stimulsoft Reports.Web 2022.2.3 Crack
  16. C语言--求质数(详解)(筛选求质数)
  17. 我是大海里的一页扁舟
  18. 报考PMP证书要花多少钱
  19. 热伤风和感冒有什么区别
  20. 穷和尚与富和尚的故事

热门文章

  1. Abbkine 彩色预染蛋白质Marker使用注意事项
  2. 冬日的傍晚,纷飞的雪花
  3. Python标准库glob模块
  4. 混合模式(工厂方法模式+策略模式+门面模式)
  5. 差分隐私相关论文集合
  6. hurricane中文_Hurricane Outbreak游戏下载_Hurricane Outbreak游戏中文版下载安装(飓风模拟器) v1.1.9...
  7. html ip 黑名单,服务器ip黑名单查询
  8. MATLAB使用教程(二)——在文件中编程——新手来看
  9. java mysql dbhelper_DbHelper通用数据库类及增删改 使用示例
  10. python数据处理之批量下载