这个查询会做你所要求的事情:
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
的行,则什么也不会插入。这可能是您想要的,也可能不是。
3. 如果
max(c_user_serial)
返回NULL,则会为
c_user_id
插入另一个NULL值。如果
c_user_id
被定义为NOT NULL,则不能发生这种情况。
为避免问题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注入的风险!
如果你想要没有间隔的序列,你必须以某种方式处理UPDATE
和DELETE
,这将不可避免地使你的序列变得混乱,这也是为什么整个想法并不好的原因之一。
另一个更重要的原因是我提到的竞争条件。对于它,没有便宜而优雅的解决方案。(有解决方案,但是或多或少都很昂贵...)
如果可能的话,请坚持使用一个普通的serial
列来存储所有行。在检索数据时,您可以始终从1开始为每个用户附加编号:
(MAX OF c_user_id where c_user_id = 1234)
没有意义 - 如果你知道c_user_id的值是1234,那么最大值肯定是1234。所以使用MAX函数没有意义。问题中是否混淆了什么? - Turophile