繁体   English   中英

使用实数在sql数据库中进行显式排序

[英]Using Real numbers for explicit sorting in sql database

我正面临一个反复出现的问题。 我要让用户重新排序存储在数据库中的一些列表。

我能想到的第一个直截了当的方法是有一个“位置”列,其顺序保存为整数。 PE

Data, Order
A     1
B     2
C     3
D     4

这里的问题是如果我必须在位置2插入FOO,现在我的桌子就变成了

Data, Order
A     1
FOO   2
B     3
C     4
D     5

所以要插入一个新行,我必须在一个包含五个元素的表上进行一次CREATE和三次UPDATE。

所以我的新想法是使用真实数字而不是整数,我的新表变成了

Data, Order
A     1.0
B     2.0
C     3.0
D     4.0

如果我想在A之后插入一个元素FOO,这就变成了

Data, Order
A     1.0
FOO   1.5
B     2.0
C     3.0
D     4.0

只执行一个SQL查询。

这对理论实数很好,但浮点数的精度有限,我想知道这是多么可行,以及是否以及如何优化它以避免超过双精度并进行合理数量的修改

编辑:

这就是我现在如何在python中实现它

@classmethod
def get_middle_priority(cls, p, n):
    p = Decimal(str(p))
    n = Decimal(str(n))
    m = p + ((n - p)/2)

    i = 0
    while True:
        m1 = round(m, i)
        if m1 > p and m1 < n:
            return m1
        else:
            i += 1

@classmethod
def create(cls, data, user):
    prev = data.get('prev')

    if prev is None or len(prev)<1:
        first = cls.list().first()

        if first is None:
            priority = 1.0
        else:
            priority = first.priority - 1.0
    else:
        prev = cls.list().filter(Rotator.codice==prev).first()
        next = cls.list().filter(Rotator.priority>prev.priority).first()

        if next is None:
            priority = prev.priority + 1.0
        else:
            priority = cls.get_middle_priority(prev.priority, next.priority)

    r = cls(data.get('codice'),
        priority)

    DBSession.add(r)

    return r

如果你想控制位置并且没有ORDER BY解决方案,那么一个相当简单和强大的方法是指向下一个或前一个。 更新/插入/删除(除了第一个和最后一个)将需要3个操作。

Insert the new Item
Update the Item Prior the New Item
Update the Item After the New Item

确定之后,您可以使用CTE(使用UNION ALL)创建一个永远不会有限制的排序列表。

我已经看到通过触发器完成的相当大的实现,以使列表保持完美的形式。 然而,我不是触发器的粉丝,只是将整个操作的逻辑放在存储过程中。

链表的想法很简洁,但按顺序提取数据却很昂贵。 如果您有一个支持它的数据库,您可以使用connect by之类的东西其拉出来。 sql中的链表是一个专门针对该问题的问题。

如果你不这样做,我在思考如何实现无限可分的范围,并想到书中的部分。 最初如何将列表存储为

1
2
3

然后在1和2之间插入一个“子部分在1下”,这样你的列表就变成了

1
1.1
2
3

如果你想在1.1和2之间插入另一个,你将第二个子部分放在1下并得到

1
1.1
1.2
2
3

最后,如果你想在1.1和1.2之间添加一些东西,你需要引入一个子分段并得到

1
1.1
1.1.1
1.2
2
3

也许使用字母而不是数字会更容易混淆。

我不确定sql数据库中是否有任何标准的词典排序可以正确排序这种类型的列表。 但我认为你可以通过一些“逐个案例”和子串的方式来推动自己。 编辑:我发现了一个与此相关的问题: linky

另一个缺点是,此解决方案的最坏情况字段大小将随着输入项的数量呈指数级增长(您可能会获得长行,如1.1.1.1.1.1等)。 但在最好的情况下,它将是线性的或几乎恒定的(行如1.934856.1)。

这个解决方案也非常接近你已经想到的,我不确定这是一个改进。 使用你提到的二进制分区策略的十进制数可能会增加每个插入之间的小数点数,对吧? 所以你会得到

1,2 -> 1,1.5,2 -> 1,1.25,1.5,2 -> 1,1.125,1.25,1.5,2

因此,分段策略的​​最佳情况似乎更好,但最糟糕的情况更糟。

我也不知道sql数据库的任何无限精度十进制类型。 但是你当然可以将你的号码保存为字符串,在这种情况下,这个解决方案变得更像原始的解决方案。

您可以使用字符串而不是数字:

item  order
A     ffga
B     ffgaa
C     ffgb

这里,有限精度的问题是通过增长弦的可能性来处理的。 理论上,字符串存储在数据库中是无限的,只有存储设备的大小。 但绝对订购项目没有更好的解决方案。 相对排序,如链接列表,可能会更好地工作(但您不能通过查询进行排序)。

将所有行设置为从1开始的唯一编号,并在开始时递增1。 插入新行时,将其设置为表的计数(*)+ 1(有多种方法可以执行此操作)。

当用户更新行的顺序时,请始终通过调用存储过程来更新它,并使用此行的Id(PK)进行更新和新订单。 在存储过程中,

update tableName set Order = Order + 1 where Order >= @updatedRowOrder;

update tablename set Order = @updatedRowOrder where Id = @pk;

这保证了总是有空间和连续的序列,没有重复。 如果你把一行愚蠢的新订单号(例如<= 0)但可能是糟糕的东西,我没有按你的方式工作。 那是为了防止前端应用程序。

干杯 -

暂无
暂无

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

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