PostgreSQL内部查询与准备好的语句

时间:2015-05-26 21:14:48

标签: php sql postgresql database-design auto-increment

我有一张表来存储联系人。

我想获取添加user_id为{some number}的列的最大值,并将其设置为当前插入记录的相同列值。

我正在使用准备好的陈述:

pg_prepare($db, "add", 'INSERT INTO '.CONTACTS.' (c_user_serial,c_name,c_company,c_email)  VALUES ($1, $2, $3, $4)');

$insert_co = pg_execute($db, "add", array({(MAX OF c_user_serial where c_user_id = 1234) + 1 increment },$name,$company,$email));

c_user_id是添加此联系人的用户的ID,还有另一列作为索引(id),这是一个常见的serial列,每列增加c_user_serial {1}}是每个用户递增的序列号。我们假设一位用户添加了一个联系人,因此它是1。在其他用户添加了多个联系人之后,当该用户添加他的第二个联系人时,我希望此列存储2,因此自动增加一列,但应该按用户增加。

不确定如何在此处使用内部查询来获取列的最大值,并使用当前插入的递增值。

1 个答案:

答案 0 :(得分:1)

此查询可以满足您的要求:

INSERT INTO contacts (c_user_serial, c_user_id, c_name, c_company, c_email)
SELECT max(c_user_serial) + 1, $1, $2, $3, $4
FROM   tbl
WHERE  c_user_id = $1;

您的原始忘记插入user_id本身,这是必需的。我加了它。

然而,这带来了几个主要问题:

  1. 当前“最大”可以是并发交易 之间 竞争条件的主题,并且不可靠。 (虽然serial对于并发访问是安全的。)

  2. 如果找不到c_user = $1的行,则插入 nothing 。可能是也可能不是你想要的。

  3. 如果max(c_user_serial)返回NULL,则为c_user_id插入另一个NULL值 如果c_user_id被定义为NOT NULL,则不会发生这种情况。

  4. 要避免问题2.和3.而是以每个新用户的1开头:

    INSERT INTO contacts (c_user_serial, c_user_id, c_name, c_company, c_email)
    VALUES (COALESCE((SELECT max(c_user_serial) + 1 FROM tbl WHERE c_user_id = $1), 1)
           , $1, $2, $3, $4)
    

    此处“no row”转换为NULL,在这种情况下,COALESCE默认为1

    简单解决方案

    一切都放在一起,简单的解决方案是:

    pg_prepare($db, "add"
         , 'INSERT INTO ' . CONTACTS . ' (c_user_serial, c_user_id, c_name, c_company, c_email)
            VALUES (COALESCE((SELECT max(c_user_serial) + 1 FROM tbl WHERE c_user_id = $1), 1)
                   , $1, $2, $3, $4)');    
    $insert_co = pg_execute($db, "add", array($user_id,$name,$company,$email));
    

    确保CONTACTS拥有正确转义的表名,或者您对 SQL注入持开放态度。

    如果您正在寻找无间隙的序列,您必须以某种方式处理UPDATEDELETE ,这将不可避免地笼罩您的序列时间,这也是原因之一,为什么整个想法不那么好

    另一个更重要的原因是我提到的竞争条件。它有没有便宜和优雅的解决方案。 (有解决方案,但或多或​​少......)

    如果可能的话,坚持所有行的一个普通serial列。在检索数据时,您始终可以从1开始为每个用户附加数字: