簡體   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