如何在数据库中存储有序的数据

思考一个问题,现在需要在前端展示和管理一个数据列表,这个列表需要让用户自己来定义顺序,并不是按照添加的时间或者其他属性来排序,那么我们如何在数据库中存储这样的数据呢?
这种场景可能大部分人都遇到过,比如股票应用,每个人关注的股票都不一样,而且都会把自己最关注的放在最前面,这样在打开应用时就能以最快的速度看到自己关注的股票的涨跌情况。
在前端进行移动排序非常容易,因为在前端一般都是用数组来存储列表数据,数组元素的增加、删除、修改顺序等操作都有对应的api,操作简便,效率也高,数据库不一样,数据库里的记录都是无序的,通过修改记录的顺序来维持用户自定义的顺序代价太高,所以需要用单独的字段来存储用户自定义的股票顺序,而且在用户添加股票、修改排序、删除股票后都需要做相应的修改。

权重字段

最便捷的一个方法就是增加一个权重字段,值的类型是整数,数值越大的排序越靠前,数值小的排到最后,这个数值其实就是用户对这些股票的关注程度。
举个例子:

item1->2
item2->1
item3->3

按照权重字段降序排列读取出来后的顺序就是item3->item1->item2
这样做有个问题,如果用户想增加一个item4,并且把它排在item1item2之间,那这样item1item3的权重都得改,数据越多,操作起来越复杂。
如果我们可以明确需要排序的数据的数据量,那么可以把权重的数之间的间隔设置更大一些,比如100,每次插入都插在前后两条记录的权重的平均数的位置。我们来看一下:

item1->200
item2->100
item3->300

现在需要插入item4,那么它的权重值设置在150比较合适,方便后续的插入。
这种设置权重字段的方式适用于小数据量的情况,而且开销小,但弊端也很明显,面对大数据量非常乏力,这时我们可以考虑用另外一种方式。

单向链表

单向链表非常适用于这个场景,增加一个字段,取名叫next,指向它的下一条数据的id,即使数据库里的记录没有顺序,我们查询时也能很轻松的把数据按照用户自定义的顺序排列出来。

item1(id:1) next->2
item2(id:2) next->-1
item3(id:3) next->1

查询出来后先找到next为-1的记录(item2)作为列表的最后一个,再查找next为2的记录……这样依次排序,就得到了最终想要的顺序列表。

增加一条记录时,需要客户端把前一条数据的id传给服务端,获取这条数据的next字段,并修改这条数据的next字段,使其指向新增加的数据的id,新数据的next字段则改为前一条数据的之前的next字段指向的id。比如增加item4,插入到item1item2之间。

item1(id:1) next->4(修改)
item2(id:2) next->-1
item3(id:3) next->1
item4(id:4) next->2(新增)

删除一条数据也需要获取前一条数据,把next字段值的值修改为被删除的这条数据的next字段指向的id。

修改一条数据顺序(即移动某一条数据)的操作稍微复杂一些,需要修改在移动之前这条数据的前一条数据的next指向,还需要修改移动之后这条数据的前一条数据的next指向,最后修改它自己的next指向。
比如,移动item4item1item3之间。

item1(id:1) next->2(item4移动之前的前一条数据)
item2(id:2) next->-1
item3(id:3) next->4(item4移动之后的前一条数据)
item4(id:4) next->1(修改)

在链表的操作过程中最多需要在数据库里修改三条记录,开销并不算大。随着数据增多,操作数据库的次数并不会增加。

“链表”转换成数组

数据库中的链表并不是真正的链表,因为next字段只是记录了下一条数据的id,不是真的指针,所以在从数据库里获取到数据后还要转换成真的链表。
具体的转换过程思路是这样:

  1. 通过读库获取数据列表
  2. 建立一个字典表,以每项数据的id为key,整项数据为value
  3. 然后遍历整个数据列表,通过next字段在字典表里找到对应的元素,然后将这两个元素互相关联,形成一个双向链表
  4. 找到数据列表中没有指向前一个元素的指针的元素,即为整个双向链表的第一项。
  5. 通过指针依次读取,把所有的数据都添加到数组中

这里以JavaScript为例来实现。
从数据库中获取的数据是这样:

[
  {id:1,name:"item1",next:4},
  {id:2,name:"item2",next:-1},
  {id:3,name:"item3",next:1},
  {id:4,name:"item4",next:2},
]

具体实现代码:

function transform(data){
  var dictionary={};

  data.forEach(function(item){
    dictionary[item.id]=item;
  });

  data.forEach(function(item){
    if(dictionary.hasOwnProperty(item.next)){
      //nextItem属性指向下一个元素
      item.nextItem=dictionary[item.next];
      //previousItem属性指向前一个元素
      dictionary[item.next].previousItem=item;
    }
  });
  
  var first = data.filter(function(item){
    return item.previousItem===undefined;
  })[0];//找到第一个元素
  
  var sortedList=[first];
  while(first.nextItem){
    sortedList.push(first.nextItem);
    first=first.nextItem;
  }
  
  return sortedList;
}

这里运用到了引用类型的特性,只需要为每个元素找到对应的nextItempreviousItem,所有的元素就会根据指针串联起来。

小结

本文一共讲了两种在数据库中存储有序数据的方式,第一种适用于小数据量的数据,第二种方式无论大数量量还是小数据量都适用,当然推荐使用第二种方式,无论数据有多少。同时还介绍了一种方法,把数据库中读取的数据转换成指定顺序的数组,前后端都可以用。
还有一种情况需要跟大家分享一下,就是在树节点的排序中使用第二种方式会比较好。同级节点之间排序使用权重字段当然是没有问题,如果存在跨层级移动节点的情况,使用单向链表的方式会更方便。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容