在下一个未使用的索引处排序插入,通用SQL

时间:2011-01-25 16:08:38

标签: sql sql-server algorithm indexing

有各种类似的问题,但他们要么提到过于具体的数据库,要么提到未分类的数据。

就我而言,如果可能,SQL应该是可移植的。有问题的索引列是一个包含时间戳的聚簇PK。

时间戳是比以前插入的值大99%的时间。但是,在极少数情况下,它可能会更小,或与现有值发生碰撞。

我目前正在使用此代码插入新值:

IF NOT EXISTS (select * from Foo where Timestamp = @ts) BEGIN
    INSERT INTO Foo ([Timestamp]) VALUES (@ts);
END
ELSE BEGIN
    INSERT INTO Foo ([Timestamp]) VALUES (
    (SELECT Max (t1.Timestamp) - 1
    FROM Foo t1
    WHERE Timestamp < @ts
    AND NOT EXISTS (select * from Foo t2 where t2.Timestamp = t1.Timestamp - 1))
    );
END;

如果该行尚未使用,请插入。否则,使用EXISTS检查找到具有较小值的最近的自由行。

对于数据库我是新手,所以我不确定是否有更好的方法。我愿意接受任何想法,使代码更简单和/或更快(每秒大约100-1000次插入),或者完全使用不同的方法。

编辑感谢您的评论和答案到目前为止。

解释我案例的性质:时间戳是用于对数据进行排序的唯一值,可以忽略轻微的不一致。没有FK关系。

但是,我同意我的方法存在缺陷,超过了首先使用所提出的想法的原因。如果我理解正确,修复设计的一种简单方法是将常规的自动增量PK列与已知(和重命名)的时间戳列结合使用,该列将被聚类。

从性能POV来看,我看不出这比初始方法更糟糕。它还简化了代码。

4 个答案:

答案 0 :(得分:4)

这种方法是灾难的处方。首先,你将有竞争条件,当他们的插入不起作用时将导致用户烦恼。更糟糕的是,如果您使用该值作为外键添加到另一个表并且整个事物不在一个事务中,则可能是将子数据添加到错误的记录中。

此外,如果您未正确设置外键关系并删除记录而未获取所有子记录,则查找最低未使用值是进一步数据完整性混乱的方法。现在你刚刚加入了不属于新记录的记录。

这种手动方法存在缺陷且不可靠。所有主要数据库都有一种创建自动生成值的方法。相反,使用它,问题已经制定和测试。

时间戳BTW是一个SQL服务器保留字,不应该用作字段名。

答案 1 :(得分:3)

一个想法是添加代理身份/自动编号/序列密钥,因此主键变为(时间戳,新密钥)。

这样,您可以保留行顺序和唯一性而无需代码

要运行上面的代码,您需要在上面的代码中摆弄锁粒度和并发提示,或者TRY/CATCH to retry with the alternate value (SQL Server).这会消除可移植性。但是,在负载很重的情况下,您必须继续重试,因为备用值可能已经存在。

答案 2 :(得分:3)

如果你不能保证你的PK值是唯一的,那么它不是一个好的PK候选者。特别是如果它是一个时间戳 - 我肯定高盛会喜欢它,如果他们的高频交易程序可能导致插入冲突并提前1微秒插入,因为系统摆弄了他们交易的时间戳。

由于您不能保证时间戳的唯一性,更好的选择是使用普通的自动增量int / bigint列来处理碰撞问题,为您提供获取插入顺序的好方法,如果需要,您仍然可以对时间戳字段进行排序以获得良好的直线时间线。

答案 3 :(得分:0)

时间戳作为关键?真?每次更新行时,都会修改其时间戳。 SQL Server时间戳数据类型旨在用于版本控制行。它与ANSI / ISO SQL时间戳不同 - 这相当于SQL Server的日期时间数据类型。

就时间戳列上的“排序”而言:唯一保证带有时间戳的是每次插入或更新行时,它都会获得一个新的时间戳值,该值是唯一的8个八位字节的二进制值,不同于分配给该行的先前值(如果有)。无法保证该值与系统时钟有任何关联。