要产生您想要的结果,您可以在result 表上使用INSERT ... SELECT 语法和COUNT 子查询,以检索您的行数或0,以及@987654334 的CONCAT() @ 生成所需的rsid 值。
为帮助防止 SQL 注入攻击,强烈建议使用prepared statements,
假设 rsid 是您的主键,您可以使用以下内容。
//enable mysqli exception handling
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$c = mysqli_connect($host, $user, $pass, $dbname);
$query = <<<'EOL'
INSERT INTO `result`
SELECT
CONCAT(?, COALESCE((SELECT COUNT(r.rsid)
FROM `result` AS r
WHERE r.mid = ?
GROUP BY r.mid), 0)),
?, ?, ?, ?
EOL;
try {
/* example data
* $m = 0;
* $u = 1;
* $w = 1;
* $k = 1;
*/
$stmt = mysqli_prepare($c, $query)
mysqli_stmt_bind_param($stmt, 'ssssss', $m, $m, $m, $u, $k, $w);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
/*
* retrieve the theoretical last record inserted
*/
$stmt = mysqli_prepare($c, 'SELECT MAX(rsid) FROM result WHERE mid = ?');
mysqli_stmt_bind_param($stmt, 's', $m);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $rsid);
if (mysqli_stmt_fetch($stmt)) {
echo $rsid;
}
mysqli_stmt_close($stmt);
} catch(mysqli_sql_exception $e) {
//handle the exception
//echo $e->getMessage();
}
每次执行的结果
| execution | $rsid |
| 1 | 00 |
| 2 | 01 |
| 3 | 02 |
...
免责声明,您当前使用总行数生成rsid 的方法会导致严重的数据完整性问题。当一个
记录从result 表中删除,检索到的COUNT 或mysqli_num_rows 将导致rsid 冲突。此外,如果 mid 值被更改(更正错字),rsid 值将变为无效。
race conditions/hazards 还需要考虑其他复杂情况。
有关预防措施的详细信息,请参阅sql - Do database transactions prevent race conditions
例如,如果您在数据库中已有00 的记录,则SELECT CONCAT(mid, COUNT(rsid)) WHERE mid = 0; 将返回01。在插入01 然后删除00 之后,你的下一个插入将是01 的重复rsid;
INSERT INTO results(rsid)
VALUES(01);
DELETE FROM results WHERE rsid = '00';
INSERT INTO results(rsid)
VALUES(01); /* error duplicate key */
我建议使用SELECT MAX(rsid) + 1 而不是COUNT()。这将确保您的rsid 在删除后不会重复,但不会解决UPDATE mid 问题。 但是,mid 至少需要 1 个,MAX() + 1 才能工作。
INSERT INTO `result`
SELECT
COALESCE((SELECT MAX(r.rsid) + 1
FROM `result` AS r
WHERE r.mid = ?
GROUP BY r.mid), CONCAT(?, 0)),
?, ?, ?, ?
如果您绝对需要使用行数,为了避免复杂性,您需要确保在mid 分组中只记录DELETE 最高的rsid,而不是UPDATE mid 列价值观。否则,您需要在对表进行任何更改时重建所有 rsid 值。如果您决定重建rsid 值,我建议使用更新前和删除后triggers 来处理这两个实例,并使用DATETIME NULL DEFAULT CURRENT_TIMESTAMP 列来确定记录的顺序。
或者,您可以仅在查看时生成rsid,方法是使用AUTO_INCREMENT 主键和增量用户变量。那么你再也不用担心应用程序控制rsid,只需插入mid即可。
SET @rsid: = NULL;
SET @mid = NULL;
SELECT
result.*,
CONCAT(mid, CASE WHEN @mid != result.mid OR @rsid IS NULL THEN @rsid := 0 ELSE @rsid := @rsid + 1 END) AS rsid,
@mid:=result.mid
FROM result
ORDER BY mid ASC, id ASC;
结果
| id | mid | ... | rsid |
| 1 | 0 | ... | 00 |
| 29 | 0 | ... | 01 |
| 311 | 0 | ... | 02 |
| 20 | 1 | ... | 10 |
| 40 | 1 | ... | 11 |
...
作为最后一种选择,您可以通过 MyISAM 引擎指定复合主键 mid, id 来使用分段的AUTO_INCREMENT,该引擎不能够进行事务或外键引用。但是,这种方法还有其他复杂性。 [sic]
如果您删除任何组中具有最大AUTO_INCREMENT 值的行,则AUTO_INCREMENT 值将被重用。
CREATE TABLE `result` (
`mid` INT(11) UNSIGNED NOT NULL,
`id` INT(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`mid`, `id`)
)
ENGINE=MyISAM
;
INSERT INTO result(mid)
VALUES(0),(1),(0),(2),(0),(1);
SELECT
result.*,
CONCAT(mid, id) AS rsid
FROM result;
结果:
| mid | id | rsid |
| --- | --- | ---- |
| 0 | 1 | 01 |
| 0 | 2 | 02 |
| 0 | 3 | 03 |
| 1 | 1 | 11 |
| 1 | 2 | 12 |
| 2 | 1 | 21 |
要更新您当前的 result 表,您可以使用
/* remove default value */
ALTER TABLE `result` ALTER `mid` DROP DEFAULT;
/* change engine, add id column, add composite primary key */
ALTER TABLE `result`
ENGINE=MyISAM,
CHANGE COLUMN `mid` `mid` INT(11) NOT NULL FIRST,
ADD COLUMN `id` INT(11) NOT NULL AUTO_INCREMENT AFTER `mid`,
ADD PRIMARY KEY (`mid`, `id`);