首页 > 解决方案 > 使用 SELECT rows by ID 生成不同的唯一 ID

问题描述

这是一个 php mysqli 查询,其中有一个名为 result(uid,matchid,playerid,score) 的表我想根据带有 matchid 的 select 语句返回的行数生成一个唯一的 id 如果 matchid 为 0,它看起来像这样然后首先返回的行将为零,然后 uid 必须是 matchid+rows 返回,然后使用该 uid 保存数据并将 id 与其他详细信息匹配

我希望它必须返回这样的东西......即如果id1(ID)为0,那么唯一的id(UID)将是ID + UID,即00,01,02......等等,UID将基于以特定 id1(ID) 返回的行数。

另一个示例,采用不同的 ID,即如果 id1(ID) 为 1,则根据上述解释,它将是 ID+UID,即 10,11,12...等等,基于该 id1( ID)

我试图获取匹配 id 返回的行数,发现给出的结果是 0(必须表示 null),并且 scound time 它也返回 0,并且在第三次之后它开始递增值。即 00,00,01,02 等等

<?php

$ri=0;
$res=mysqli_query($c,"select rsid from result where mid='$m' ");

$rows=mysqli_num_rows($res);

$ri=$rows++;

echo ("<script> alert( '$ri' ); </script>");

//Unique Match Id for Player Result
$rsid = $m.$ri;

//Saving the data in the table
mysqli_query($c,"insert into result values('$rsid','$m','$u','$k','$w')");

?>

我预计输出为 00,01,02... 但输出为 00,00,01,02...

标签: phpmysqli

解决方案


为了产生你想要的结果,你可以在表上使用INSERT ... SELECT带有COUNT子查询的语法result,以检索你的行数或 0,以及CONCAT()产生mid想要的rsid值。

为了帮助防止 SQL 注入攻击,强烈建议使用准备好的语句

假设rsid是您的主键,您可以使用以下内容。

示例db-fiddle

//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,检索到的COUNTmysqli_num_rows将导致冲突rsid。此外,如果mid值被更改(更正错字),该rsid值将变为无效。

比赛条件/危险还需要考虑其他复杂情况。有关预防措施的详细信息,
请参阅sql - Do database transactions prevent race conditions

例如,如果您00在数据库中已有记录,SELECT CONCAT(mid, COUNT(rsid)) WHERE mid = 0;将返回01. 插入01然后删除后00,您的下一个插入将是rsid;01的副本

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问题。但是,您至少需要mid1MAX() + 1才能工作

示例db-fiddle

INSERT INTO `result`
SELECT 
COALESCE((SELECT MAX(r.rsid) + 1
  FROM `result` AS r
  WHERE r.mid = ?
  GROUP BY r.mid), CONCAT(?, 0)), 
?, ?, ?, ?

如果您绝对需要使用行数,为了避免复杂性,您需要确保只使用分组DELETE中的最高rsid记录,而不是列值。否则,您将需要在对表进行任何更改时重建所有值。如果您决定重建这些值,我建议使用更新前和删除后处理这两个实例并使用列来确定记录的顺序。midUPDATEmidrsidrsidtriggersDATETIME NULL DEFAULT CURRENT_TIMESTAMP


或者,您可以rsid通过使用AUTO_INCREMENT主键和增量用户变量生成仅在查看时。那么你就不再需要担心应用程序受控了rsid,只需要插入即可mid

示例db-fiddle

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 引擎AUTO_INCREMENT指定复合主键来使用分段,该引擎支持事务或外键引用。但是,这种方法还有其他复杂性。[原文如此]mid, id

AUTO_INCREMENT如果删除AUTO_INCREMENT任何组中值最大的行,值将被重用。

示例db-fiddle

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`);

推荐阅读