mem*_*can 6 database-design sql-server
我在应用程序设计中有一个常见的场景,我需要存储一个有序列表,并通过应用程序 UI 轻松访问和调整排序。
排序操作很简单,在一个时间上只有一个项目操作-移动到顶部,移至底部,移动了一个位置,或者下一个位置。这些定位操作可能穿插在典型的添加/编辑/删除列表操作中。为了讨论,这是一个单用户应用程序和数据库存储。
因为对象可能非常复杂,我想避免缓存整个列表客户端,并在内存中跟踪新创建的对象,直到发生大的保存操作。
我几乎只在这些项目中使用 MS-SQL,我想知道是否应该使用数据库功能、SQL 功能或设计模式来更好地支持有序列表的管理。
我目前的方法是使用一DECIMAL
列来存储列表位置,这些位置在项目移动过程中变为小数。然后我对列表进行排序并重新生成整数位置以规范化列表。在此操作之后,它已准备好进行另一项移动。
在示例实现中,我创建了一个名为Seq
位置跟踪的列。一次小数位足以用于 0.5 存储。
[Seq] [decimal](10, 1) NOT NULL
Run Code Online (Sandbox Code Playgroud)
当一个项目在应用程序 UI 中重新定位时,
UPDATE
用新的Seq
价值记录Seq
并Seq
从基于 1 的整数系列重新生成表中的所有值新Seq
值很容易计算-
随着列表变长,更新变慢,因为我正在执行 N 个UPDATE
语句,其中 N 是表中的行数,因此它似乎只适用于小表。
一种更高效的方法是允许更多的小数位,然后定位在两个相邻项目的中间位置,在列表上没有重新排序。
这种方法只需要一个SELECT
(最多包含 2 行)和一个UPDATE
.
但是,如果有足够的重新定位,我想该字段会用完小数位。例如,如果我从位置 10 和 11 处的项目开始,并从位置 9 继续向下移动列表中的项目,它们将被赋予 10.5、10.25、10.125 的序列。10 次移动后,仓位变长... 10.0009765625。
更新 #1 - 改进了列表的重新排序
对于重测序,我更赶上上row_number()
,OVER
和WITH
在MS-SQL的功能。很高兴发现有一种更流畅的方法可以在单个UPDATE
操作中重新排序列表;
with OrderedItems as
(
select
ItemID,
Seq,
row_number() over (order by Seq asc) NewSeq
from
Items
)
update OrderedItems
set Seq = NewSeq
Run Code Online (Sandbox Code Playgroud)
最后一种是经常使用的技术,而且很有效。如果您担心用完小数位,您可以对其进行改进,以检测下两个前一个/后一个值之间的平均值何时无法与其中之一区分开(即:您用完了小数位)。
发生这种情况时,您应该根据需要向上或向下重新排序尽可能多的行。
假设您只使用 2 位小数,并且有两行分别为Seq
1.00 和 1.01;下一个值为 2.00。如果您想使用您的技术在这两者之间移动一行,则Seq
= (1.00, 1.005 , 1.01, 2.00)。这将需要比可用的小数更多的位数,并且根据舍入规则,1.005 将舍入为 1.00 或 1.01。在任何情况下,您都可以检测到插入的值与 1.00 或 1.01 无法区分。
在这种情况下,您需要“向下(或向上)推并腾出空间”。也就是说,您应该再更改一行,并得到 (1.00, 1.25, 1.50 , 2.00)。您已将更改分散到两行,而不是仅仅一行。
如果您遇到诸如 (1.01, 1.02, 1.03, 1.04, ...) 之类的情况,这可能需要递归(或迭代)地完成。
通过对原始技术的扩展,您无需重新规范化。这仅在“根据需要”的基础上进行处理,并且可以很好地扩展。
笔记:
实现该算法可能有点棘手,因为您需要考虑何时位于第一行或最后一行,以及如何处理这些,以及数字何时Seq
达到最大或最小可用值。
该算法适用于任意数量的小数,包括 0。也就是说,您也可以将它与整数值一起使用decimal
,而不是 ,这可能会证明计算效率更高,或者根据decimals
实现方式,也可以提高空间效率。
归档时间: |
|
查看次数: |
1568 次 |
最近记录: |