-2

It's a php mysqli query where there is a table named result(uid,matchid,playerid,score) I want to generate a unique id based on number of rows returned by the select statement with matchid That would look something like this if matchid is 0 then on first the rows returned would be zero then uid must be matchid+rows returned and then saving data with that uid and match id with other details

I hoped it must return something like this... i.e. if id1(ID) is 0 then the unique id(UID) would be ID+UID that is 00,01,02...and so on where the UID would increment based on number of rows returned with the specific id1(ID).

another example, taking a different ID i.e. if id1(ID) is 1 then according to the above explanation it would be ID+UID i.e. 10,11,12...and so on based on the number of rows returned by that id1(ID)

I have tried to get the number of rows returned by the match id and found that the result given was 0(must indicates null),and scound time it also returns 0 and after the third time it starts incrementing values. i.e 00,00,01,02 and so on

<?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')");

?>

I expected the output to be 00,01,02... but the output is 00,00,01,02...

Robert Price
  • 611
  • 4
  • 11

1 Answers1

0

To produce your desired results, you can use the INSERT ... SELECT syntax with a COUNT subquery on the result table, in order to retrieve your row count or 0, along with a CONCAT() of the mid to produce the desired rsid value.

To help prevent SQL injection attacks, it is strongly encouraged to use prepared statements,

Assuming rsid is your primary key you can use the following.

Example 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();
}

Results per execution

| execution | $rsid |
|     1     |  00   | 
|     2     |  01   | 
|     3     |  02   |
...

Disclaimer, your current approach of using the total number of rows to generate the rsid is subject to serious data integrity complications. When a record is deleted from the result table, the retrieved COUNT or mysqli_num_rows will result in a conflicting rsid. Additionally if the mid value is ever changed (a typo is corrected), the rsid value will become invalid.

There are also additional complications to consider with race conditions/hazards.
See sql - Do database transactions prevent race conditions for details on preventative measures

For example, if you have a record of 00 in the database already, SELECT CONCAT(mid, COUNT(rsid)) WHERE mid = 0; will return 01. After inserting 01 and then removing 00, your next insert will be a duplicate rsid of 01;

INSERT INTO results(rsid)
VALUES(01);
DELETE FROM results WHERE rsid = '00';
INSERT INTO results(rsid)
VALUES(01);  /* error duplicate key */

I recommend using SELECT MAX(rsid) + 1 instead of COUNT(). This will ensure your rsid is not repeated after deletion, but does not resolve the UPDATE mid issue. However you will need to have a minimum mid of 1 for MAX() + 1 to work.

Example 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)), 
?, ?, ?, ?

If you absolutely need to use row count, to avoid the complications, you would need to ensure to only DELETE the highest rsid record within the mid grouping, and never UPDATE the mid column values. Otherwise you will need to rebuild all rsid values, on any change to the table. If you decide to rebuild the rsid values, I suggest using before update and after delete triggers to handle both of these instances and use a DATETIME NULL DEFAULT CURRENT_TIMESTAMP column to determine the ordering of the records.


Alternatively you could generate an rsid only when viewed, by using an AUTO_INCREMENT primary key and an incremental user variable. Then you no longer need to worry about the application controlled rsid, and only be required to insert the mid.

Example 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;

Result

| id  | mid | ... | rsid |
| 1   |  0  | ... |  00  |
| 29  |  0  | ... |  01  |
| 311 |  0  | ... |  02  |
| 20  |  1  | ... |  10  |
| 40  |  1  | ... |  11  |
...

As a last alternative, you can use a segmented AUTO_INCREMENT by specifying a composite primary key of mid, id with the MyISAM engine, which is not capable of transactions or foreign key references. However, there are additional complications with this approach. [sic]

AUTO_INCREMENT values are reused if you delete the row with the biggest AUTO_INCREMENT value in any group.

Example 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;

Results:

| mid | id  | rsid |
| --- | --- | ---- |
| 0   | 1   | 01   |
| 0   | 2   | 02   |
| 0   | 3   | 03   |
| 1   | 1   | 11   |
| 1   | 2   | 12   |
| 2   | 1   | 21   |

To update your current result table you can use

/* 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`);
Will B.
  • 17,883
  • 4
  • 67
  • 69