寻找值内的下一个差距

时间:2012-04-23 01:32:58

标签: sql orm

假设我有下表:

id  name  base   index
0   A     2      0
1   B     2      2
2   C     2      4
3   D     2      6
4   E     2      8
5   F     2      10

所以,index = base * i,其中i是序列中该行的位置。

有时会删除某些行,例如,如果我删除名为C和D的行:

id  name  base   index
0   A     2      0
1   B     2      2
4   E     2      8
5   F     2      10

总是在最后一行之后添加新行,因此在这种情况下,下一行将是MAX(索引)+ base = 12,但是由于删除的行而导致索引列中值之间的间隙将是一个问题一段时间如果不是最后插入而是将其插入第一个可用间隙,则问题不会发生。

所以,我怀疑找到第一个可用间隙的任何查询都与MAX(索引)一样有效,但最有效的解决方案是什么?也许这已经足够了。

如果不清楚,我需要找到第一行'a',使得索引值最高的行超过a.index + a.base。

这适用于对任何SQL数据库使用ORM的应用程序,因此它必须是严格标准的SQL。

修改

这是真实表格和真正问题的简化,我正在寻找仅使用基本列和索引列的解决方案。涉及在其他表中添加新列或索引的解决方案对我的应用程序而言并不实用。

编辑2

基础专栏似乎使它变得更加复杂,但这并不重要。问题可以简化为如下表:

id  name  index
0   A     0
1   B     1
4   E     4
5   F     5

我需要找到第一行'a',使得索引最低的行高于a.index + x。在这种情况下,x = 1。

不首先排序或利用id进行枚举不是可靠的解决方案,因为这些可以改变。例如,如果行也是这样的话,解决方案必须工作:

id  name  index
0   A     0
23  F     5
45  E     4
90  B     1

4 个答案:

答案 0 :(得分:1)

如果表中有多个“base”值,我的问题意味着什么并不清楚。 “具有最接近的上部索引值的行”是否必须具有相同的“base”值,例如?

在任何情况下,如果您使用的是实现LEAD()函数的SQL平台,那么这可能是一个开始。你可能不得不用适当的方言改写TOP。将999999999替换为大于index + base的最大可能值的任何值。

with LeadAdded as (
  select 
    lead(index,1,999999999) over (order by index) as nxt,
    *
  from yourTable
)
  select top (1) *
  from LeadAdded
  where nxt > index + base;
  order by index

答案 1 :(得分:0)

您可以添加另一列以将其标记为可用,而不是删除该行吗?然后,您可以从表中选择具有给定基数的标记为“AVAILABLE”的MIN(id)。如果没有找到,那么你插入。那样你就可以避免出现差距,保留历史记录,也许可以简化?

答案 2 :(得分:0)

大多数SQL方言都支持窗口函数,因此您可以执行以下操作:

select min(id)
from 
(
   select t.*, 
      row_number() over (order by id) as rownum
   from t
)
where id <> rownum

这将返回不按顺序的第一个id。

我可能会建议,类似于第一个建议。删除行时,将id存储在另一个“可用”ID表中。插入时,请先查看此表。如果没有,那么创建一个新的。

答案 3 :(得分:0)

嗯,一种不依赖于标准SQL以外的方法的方法是保留一个单独的表“index的所有可能值”:

SELECT * FROM indices LIMIT 7;
+------+
| idx  |
+------+
|    0 |
|    2 |
|    4 |
|    6 |
|    8 |
|   10 |
|   12 |
+------+
7 rows in set (0.00 sec)

然后,假设您的用户表看起来像这样,第一个差距发生在index = 4:

SELECT * FROM users;
+------+------+------+------+
| id   | name | base | idx  |
+------+------+------+------+
|    0 | A    |    2 |    0 |
|    1 | B    |    2 |    2 |
|    4 | E    |    2 |    8 |
|    5 | F    |    2 |   10 |
+------+------+------+------+
4 rows in set (0.00 sec)

您可以使用带有索引表的LEFT JOIN来找到第一个差距:

SELECT indices.*
FROM indices
LEFT JOIN users
USING(idx)
WHERE users.idx IS NULL
ORDER BY idx
LIMIT 1;

+------+
| idx  |
+------+
|    4 |
+------+
1 row in set (0.00 sec)

如果在索引表结束后出现第一个间隙,则会失败,在这种情况下,您可以检测错误并扩展索引表。