如何在SQL中为多个表实现全局递增ID?

时间:2011-10-13 08:23:45

标签: php mysql sql database-design

我在编码时决定为每种类型的内容提供不同的表格。现在我无法解决这个问题。基本上我的通知系统按当前时间戳排列最新内容。这是不准确的,因为有人很可能会与另一个人同时提交内容,并且会出现错误的排名。

现在,如果我将所有内容都放在一个表中,我只需将其按自动递增变量进行排名即可。有没有办法在多个表中实现这个自动增量整数(例如,当某些内容插入到 table1 中时,id = 0,某些内容被插入 table2 ,id = 1 )。或者我必须将所有内容重新编码到一个表中。

注意:

我在多个表中有内容的原因是因为它有条理并且它会减少负载压力。我不再关心组织,因为我可以通过我编码的GUI访问数据,我只是想知道负载压力。

修改

我正在使用PHP 5和MySQL。

4 个答案:

答案 0 :(得分:4)

您的问题,特别是跨越多个表的ID需求,显然表明您的数据库设计需要改变。您应该为所有内容类型创建一个表(作为一般化),并使用自动增量ID。然后,对于每种特定的内容类型,您可以使用额外字段定义其他表(相当于OOP中的继承),并将外键指向基本表。

换句话说,你需要像inheritance in SQL这样的东西。

答案 1 :(得分:2)

在单个表格中,您可以为包含content type字段的clusteredcontent type索引添加字段。这有效地将所有一种内容类型保存在光盘上的一个位置,将另一种内容类型保存在另一个地方等等(它实际上组织成页面,但这种物理组织仍然是真实的。)

假设每个内容类型具有相同的字段,这可能会满足您的需求的行为与多个表类似。在某些情况下,您甚至可能会发现,使用适当的索引,单个表解决方案可以更快,更方便和可维护等。例如尝试在所有内容类型中创建全局唯一标识符。


如果您无法将这些合并回单个表格中,可以创建中央链接表...

CREATE TABLE content_link (
  id            INT IDENTITY(1,1),         -- MS SQL SERVER syntax
  content_type  INT,
  content_id    INT                        -- The id from the real table
)

在插入内容表时,还要插入链接表以创建全局唯一ID。


更简单,但更手动,只需在数据库中的某个位置保留一个值即可。每当您需要新的id时,请使用该集中存储的值并将其递增1。务必将增量和集合包装在单个事务中以停止竞争条件。 (这可以通过多种方式完成,具体取决于您的SQL风格。)

修改

来自网络的几个MySQL示例代码行......

START TRANSACTION;
INSERT INTO foo (auto,text)
    VALUES(NULL,'text');         # generate ID by inserting NULL
INSERT INTO foo2 (id,text)
    VALUES(LAST_INSERT_ID(),'text');  # use ID in second table
COMMIT TRANSACTION;


就个人而言,我实际上将值存储在变量中,提交事务,然后继续我的业务逻辑。这将使表上的锁保持最小。

答案 2 :(得分:1)

您可以创建一个自动增量ID为的表,以便跟踪ID。你的程序会在该表上插入,获取id,必要时使用它。

有些事情:

function getNextId() {
    $res = mysql_query("INSERT INTO seq_table(id) VALUES (NULL)");
    $id = mysql_insert_id();
    if ($id % 10 == 0) {
        mysql_query("DELETE FROM seq_table");
    }
    return $id;
}

其中seq_table是您为了获取ID而创建的表。使其成为一种功能,以便您可以随时使用。生成的每10个ID我删除所有生成的ID,无论如何你不需要它们。我不会每次都删除,因为它会减慢。如果在此期间发生了另一个插入,并且我删除了11个或更多记录,则不会影响此过程的行为。它必须达到目的是安全的。

即使表是空的,新的ID也会继续增长,因为你已经将id声明为自动增量。

更新:我想澄清为什么ID生成没有包含在事务中以及为什么不应该包含它。

如果生成自动ID并回滚交易,则下一个自动ID将会增加。摘录自MySQL bug report

  

[...]这不是一个bug,而是我们所知道的每个RDBMS中发生的预期行为。生成的值不是事务的一部分,也不关心其他语句。

使用此过程获取ID完全是线程安全的。获取ID后的逻辑应该包含在事务中,尤其是在处理多个表时。

以这种方式获取序列并不是一个新概念,例如,作为稳定数据库访问库的metabase_mysql.php代码有一个名为GetSequenceNextValue()的方法,它非常相似。

答案 3 :(得分:1)

您可以拥有一个单独的ID表,插入其中,然后使用新插入的ID。

e.g。

CREATE TABLE ids (INT UNSIGNED AUTO INCREMENT PRIMARY KEY, timeadded DATETIME);

在剧本中:

<?php
$r = mysql_query('INSERT INTO ids (timeadded) VALUES (NOW())');
$id = mysql_insert_id();
mysql_query("INSERT INTO someOtherTable (id, data) VALUES ('$id', '$data)");

添加错误检查等。

MySQL手册说明:

  

生成的ID在服务器上维护   每个连接基础。这意味着返回的值   给定客户端的函数是生成的第一个AUTO_INCREMENT值   对于影响AUTO_INCREMENT列的最新语句   客户。此值不受其他客户端的影响,即使它们也是如此   生成自己的AUTO_INCREMENT值。此行为可确保   每个客户端都可以检索自己的ID而无需担心   其他客户的活动,无需锁或   交易。

Source)所以我不认为对ACID complians的担忧是一个问题。