【问题标题】:Fill sequence in sql rows在 sql 行中填充序列
【发布时间】:2011-02-01 13:03:19
【问题描述】:

我有一个存储一组属性并保持它们按顺序排列的表。有可能会从表中删除其中一个属性(行),并且应该压缩位置序列。

例如,如果我最初有这些值:

+----+--------+-----+
| id | name   | pos |
+----+--------+-----+
|  1 | one    |   1 |
|  2 | two    |   2 |
|  3 | three  |   3 |
|  4 | four   |   4 |
+----+--------+-----+

并且第二行被删除,所有后续行的位置都应该更新以缩小差距。结果应该是这样的:

+----+--------+-----+
| id | name   | pos |
+----+--------+-----+
|  1 | one    |   1 |
|  3 | three  |   2 |
|  4 | four   |   3 |
+----+--------+-----+

有没有办法在单个查询中进行此更新?我怎么能这样做?

PS:我很欣赏 SQLServer 和 Oracle 的示例,因为系统应该支持这两种引擎。谢谢!

更新: 这样做的原因是允许用户随意修改位置,以及添加或删除新行。位置会显示给用户,因此,这些位置应始终显示一致的序列(并且必须存储此序列,而不是按需生成)。

【问题讨论】:

  • 这可能会消耗大量资源,本质上您所做的是排序,然后使用其订单号更新每一行。当您有 300 万条记录并删除第 1 行时会发生什么?必须发布 2,999,999 次更新!您想在这里实现什么 - 也许我们可以帮助您设计一些更好的东西。
  • 嗯,你说得有道理。但是该表不应该包含超过几百行,因此性能不会受到太大影响(此外,此查询可能是另外一百个查询的一部分,因此性能已经搞砸了)。
  • 你不会在这里获得并发。如果会话 A 删除“两个”同时会话 B 删除“三个”,您将遇到锁定问题。如果是批处理作业,那可能不是问题。

标签: sql-server oracle sequences


【解决方案1】:

不确定它是否有效,但使用 Oracle 我会尝试以下方法:

update my_table set pos = rownum;

【讨论】:

  • 工作就像一个魅力:) 但这是假设行是按照它们所属的顺序插入的。如何指定我希望行排序的顺序?例如,如果按 4、2、1、3 的顺序插入行(并且 pos 等于行 ID),并且我删除了第 2 行,那么这个查询将分配像 4=1、1=2、3=3 这样的序列,而不是4=3, 1=1, 3=2。
  • 您可以在 Oracle 中更新简单视图。也许使用Update (select * from my_table order by id) set pos = rownum
  • 该死的。看起来很有希望,但没有奏效。 ORA-01732 说 - “在这个视图上数据操作操作不合法”。 :(
  • 然后:update my_table outer set pos = (select r from (select rownum r, t.* from my_table) where id = outer.id ) 但这可能非常非常慢。
  • 好的,现在它工作了 :) 我还是对其进行了一些编辑以保留排序:update my_table outer set pos = (select r from (select rownum r, t.id from my_table t order by t.pos) where id = outer.id )。谢谢@Benoit!
【解决方案2】:

这可行,但对于大型数据集可能不是最理想的:

SQL> UPDATE my_table t
  2     SET pos = (SELECT COUNT(*) FROM my_table WHERE id <= t.id);

3 rows updated

SQL> select * from my_table;

        ID NAME              POS
---------- ---------- ----------
         1 one                 1
         3 three               2
         4 four                3

【讨论】:

    【解决方案3】:

    您真的需要序列值是连续的,还是只需要能够显示连续值?最简单的方法是让实际序列变得稀疏,并根据顺序计算排名:

    select id, 
           name, 
           dense_rank() over (order by pos) as pos,
           pos as sparse_pos 
    from my_table
    

    (注意:这是一个特定于 Oracle 的查询)

    如果您首先使位置稀疏,这甚至会使重新排序更容易,因为您可以使每个新位置介于两个现有位置之间。例如,如果你有一个这样的表:

    +----+--------+-----+
    | id | name   | pos |
    +----+--------+-----+
    |  1 | one    | 100 |
    |  2 | two    | 200 |
    |  3 | three  | 300 |
    |  4 | four   | 400 |
    +----+--------+-----+
    

    当需要将 ID 4 移动到位置 2 时,您只需将位置更改为 150。


    进一步解释:

    使用上面的示例,用户最初会看到以下内容(因为您正在屏蔽位置):

    +----+--------+-----+
    | id | name   | pos |
    +----+--------+-----+
    |  1 | one    |   1 |
    |  2 | two    |   2 |
    |  3 | three  |   3 |
    |  4 | four   |   4 |
    +----+--------+-----+
    

    当用户通过您的界面指示位置 4 的记录需要移动到位置 2 时,您将 ID 4 的位置更新为 150,然后重新运行查询。用户看到这个:

    +----+--------+-----+
    | id | name   | pos |
    +----+--------+-----+
    |  1 | one    |   1 |
    |  4 | four   |   2 |
    |  2 | two    |   3 |
    |  3 | three  |   4 |
    +----+--------+-----+
    

    这不起作用的唯一原因是用户直接在数据库中编辑数据。不过,即使在这种情况下,我也倾向于通过视图和触发器来使用这种解决方案。

    【讨论】:

    • 不幸的是,用户可以操纵位置,所以他们应该将实际序列存储在数据库中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2018-10-25
    • 2020-06-25
    • 1970-01-01
    相关资源
    最近更新 更多