繁体   English   中英

使用jquery支持重新排序html表行以及快速生成SQL后端的最佳方法?

[英]Best way to support reordering html table rows using jquery as well as make SQL backend fast?

我有一个asp.net-mvc网站,我有一个html数据表,其中一列是排名,表示该行的优先级(每行代表一个请求)。 我允许人们使用新数字连续编辑数据,但它实际上并不影响任何其他行(例如,没有任何东西阻止人们输入另一行中存在的alraedy值)

我正在寻找一个更有效的前端和快速后端的解决方案。 我将分解每个:

  1. 前端:

我想要一些功能,我可以上下拖动行或点击向上/向下箭头,或者当我连续输入排名值时,它会更新所有其他行的排名。 我基本上不希望用户必须更新每一行,如果有一个排名为#1的新条目(基本上必须更新前一个1 => 2,前一个2 => 3,前三个=> 4等......

是否有任何jquery库或有用的模式,以帮助支持此功能(以避免必须从头开始连接),因为这看起来像我可以想象许多其他人使用的通用功能。

作为参考,如果有人熟悉JIRA Greenhopper规划板 ,这将是一个理想的模拟实例。

  1. 后端

我的相关问题是,当我更新后端时,我试图弄清楚如何避免这种操作真的很慢。 如果我的数据库表中有一个排名字段,有人将项目更新为第一,我基本上必须在每一行上运行更新查询,将值更改为现有值+ 1.是否有一些模式可以执行此操作更有效?

对于前端看看jQueryUI的Sortable功能。 它通常应用于<ul/><ol/>元素,但如果需要,快速Google搜索也会引导指南<table/>元素进行调整。

一个插入级联到许多更新的后端问题可以通过将其视为链接列表来解决。

我假设您将数据存储为:

Rank | Key
1    | Zebra
2    | Pig
3    | Pony

因此,插入新行可能需要更新可能的每个现有行:

Rank | Key
1    | Penguin * Inserted
2    | Zebra   * Updated
3    | Pig     * Updated
4    | Pony    * Updated

相反,通过将它们与表中的另一行相关联来存储它们的顺序。

Key | ComesAfter
Zebra | null
Pig   | Zebra
Pony  | Pig

因此,插入新行最多只需要一次更新。

Key     | ComesAfter
Penguin | null    * Inserted
Zebra   | Penguin * Updated
Pig     | Zebra   * Unchanged
Pony    | Pig     * Unchanged

当然,您可以使用ID


然而

您可能会过早地进行优化; 即使在发布许多更新时,一些简单的SQL工作也可能证明是非常有效的。 考虑一下:

Key   | Rank
Zebra | 3
Pig   | 1
Pony  | 2

请注意,Value在此处充当自然键,它也可能具有ID(与Rank不同)。

要将新行添加为Rank 1,您需要执行以下操作:

begin tran
  insert values ('Penguin', 1)
  update table set Rank = Rank + 1 where Rank >= 1
commit

添加索引和可能的分区(取决于表的大小)将提供很好的性能改进 - 唯一的权衡是更偶尔的数据库管理。

这里的主要问题是你需要哪个方面:快速读取,快速写入或简单设计? 你可以有三个中的两个:)

对于你的后端:你的问题纯粹是猜测吗? 或者你真的尝试过什么?

如果您的表在其“排名”列上有索引,那么运行查询应该没有问题,例如:

UDPATE table SET rank=rank+1 WHERE rank >= {begin value} AND rank <= {end value};

即使您的表达到1M行范围,您的查询也会影响数千行。

您需要根据需要调整索引 - 例如,如果您的查询实际上是:

UDPATE table SET rank=rank+1 WHERE listID = {given list ID} AND rank >= {begin value} AND rank <= {end value};

你应该索引listID, rank (按此顺序)。

对于前端:如在其他答案中所述,阅读jqueryUI可排序小部件的文档

对于向上和向下移动项目,您可以使用jQuery UI进行排序 最简单的解决方案是在所有项目中添加一个名为score的浮点数,可能是double。 众所周知,这是一种更有效的技术,可以在订购时保持一组有序元素的变化。 必须在从DB到表示层的所有层中保留分数。 当你需要在另外两个元素之间放置一个元素时,你只需将它的浮点数设置为两个元素的平均值,这样你就可以确定它位于它们之间。

您可以从数据库中检索按其分数排序的所有项目。

您使用两个假项目(未呈现,未插入数据库,但纯虚拟)开始算法,其分数为0和1.所有“真实”项目将插入它们之间。 因此,您的第一个元素将获得0.5分,依此类推。

您必须在DB中存储您达到的两个分数之间的最小距离。 当这个数字变得太小并且接近最小精度你可能得到一个双倍时,你必须通过分配分数来重组你的表,这样两个连续元素之间的所有分数距离是相同的并且等于1 /(东西的个数)。

如果您需要向用户显示“经典排名”1,2,3 ......您可以在呈现页面之前在网页中创建它,并在每次修改用户之后立即创建。 更新所有排名的成本等于屏幕中显示的项目。 这不是一个很大的原因因为浏览器必须重新渲染屏幕上的所有元素,其成本高于重写屏幕上所有排名的成本,因此不会降低性能。 ......重要...如果您有虚拟滚动或分页...等级重写只涉及屏幕上的活动元素,因为等级的唯一目的是向用户显示,因为所有计算都是通过得分。

以下是后端的几个想法。

使后端的值如下:1 - 排名#1 100 - 排名#2 200 - 排名#3等...

这将允许您在其他两个之间添加一些值时更新最小行数。

例如,如果要在排名#3处插入值,则只需输入值150即可。

向用户显示数据时,您不会在数据库中显示实际值,而是使用ROW_NUMBER函数以用户友好的方式显示它。

当您想在地点#1添加某些内容时,您可以输入一个小于现有首位的值。 只要订单正确,实际值最终看起来像-250并不重要,ROW_NUMBER函数将负责演示。

您偶尔需要做的就是重新排序值,以便两者之间有足够的空间。

扩展@ Dwoolk的答案,您可以使用浮动进行排序。 那么你应该总是(至少很长一段时间)能够在两个现有值之间找到新值。

Angularjs在处理这些事情的“布线”方面非常棒。 这是一个例子,由Francesco建议的算法实现,Angular控制器处理items / score和表之间的所有绑定以及处理排序/重新评分的指令:

angular.module("listApp", []).
  controller("listController", function($scope) {
    var sortByKey = function(arr, key) {
      arr.sort(function(a, b) {
        if (a[key] > b[key]) {
          return -1;
        } else if (b[key] > a[key]) {
          return 1;
        } else {
          return 0;
        }
      });
    };
    $scope.items = [{id: 1111, name: "Item 1", score:0.2},
                    {id: 3333, name: "Item 2", score:0.5},
                    {id: 5555, name: "Item 3", score:0.7}];
    sortByKey($scope.items, "score");
  }).
  directive("sortable", function() {
    return function(scope, element) {
      var startIndex = null;
      var stopIndex = null;
      var averageOfNeighbors = function(items, index) {
        var beforeScore = index === 0 ? 1 : items[index -1].score;
        var afterScore = index === items.length - 1 ? 0 : items[index + 1].score;
        return (beforeScore + afterScore) / 2;
      }
      element.sortable( {
        start: function(e, ui) {
          startIndex = ui.item[0].sectionRowIndex;
        },
        stop: function(e, ui) {
          stopIndex = ui.item[0].sectionRowIndex;
          if (stopIndex !== startIndex) {
            var toMove = scope.items.splice(startIndex, 1)[0];
            scope.items.splice(stopIndex, 0, toMove);
            toMove.score = averageOfNeighbors(scope.items, stopIndex);
            console.log("Set item(" + toMove.id + ").score to " + toMove.score);
            scope.$apply();
          }
        }
      });
    }
  })

然后,这可以简单地绑定到这样的表:

    <tbody sortable>
      <tr ng-repeat="item in items">
        <td>{{item.name}}</td>
        <td>{{$index + 1}}</td>
        <td>{{item.score}}</td>
      </tr>
    </tbody>

这是一个Plunker。

另请注意,在每次重新排序后,可以进行AJAX调用以更新数据库中的单个记分记录。

我在我正在进行的项目中完成了类似的功能。

  • 对于前端:我使用jQuery before()after() API来移动行,例如:

    $(prevRow)。之前(currentRow)

  • 对于后端:我在数据库中有一个int列,用于存储每条记录的订单。

现在,一旦行排列在前端,所有行的新序列将通过Ajax发送到数据库,并且所有记录都会更新(在我的情况下,只需要更新表上的过滤记录)。

这可能不是最好的解决方案,但我找不到任何其他方法来使这项工作。

是完整实现代码的一个很好的例子。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM