Al jaren gebruik ik een (misschien verkeerde) manier van INSERT INTO namelijk...

Om een uniek veld af te dwingen (bijv. user o.b.v. e-mail) zet ik een kolom op UNIQUE en is de insert query niet true, dan toon ik een foutmelding. Sinds PHP8.x geeft dit echter een foutmelding en twijfel ik of dit altijd wel de juiste manier is geweest en vraag ik me af...

Moet ik o.b.v. de UNIQUE kolom eerst een SELECT doen om te zien of de waarde bestaat en o.b.v. daarvan een foutmelding tonen of zijn er betere manieren die misschien minder omslachtig zijn?
Database structuur is inderdaad uniek.
Als je record niet unique is dan krijg je nu inderdaad een melding. Deze melding kan je onderdrukken door
mysqli_report(MYSQLI_REPORT_OFF);

Een ander alternatief in aan je sql dit toevoegen.
ON DUPLICATE KEY UPDATE id=id
Niet echt proper maar het werkt wel.
Meldingen onderdrukken lijkt me nooit handig en in het geval van de toevoeging: is de uitkomst dan niet altijd true?
Met een UNIQUE index bewaakt de database server de integriteit van de gegevens.
Dat is ideaal om te voorkomen dat programmeurs fouten maken die de gegevens corrumperen.

De SQL syntax ON DUPLICATE KEY UPDATE is wel proper, mits je het toepast in de juiste situaties.
Stel, iemand vult de gegevens in van een bepaalde productcode (primary key) en klikt op 'Opslaan'.
Als het niet uitmaakt dat het product al in de database stond, dan kan je op deze manier de gegevens overschrijven, zonder extra queries uit te laten voeren.
Deze bewerking wordt ook wel 'UPSERT' genoemd: UPDATE OR INSERT. Zie ook https://linuxhint.com/upsert_mysql/
Hier een uitgebreid doch versimpeld voorbeeld van INSERT ON DUPLICATE UPDATE.

De idee is om een uniek accountnummer aan te maken en te reserveren alvorens de verdere gegevens in te voeren en te bewaren.

Als een leeg record niet wordt gebruikt omdat de invoer is afgebroken, kan je het gereserveerde accountnummer zelf gebruiken en daar je eigen gegevens aan toevoegen.Weliswaar is dat een omweg, maar toch.

De nadruk ligt op het aanmaken van het unieke accountnummer mbv MySQL in spCreateAccount().

Wat het doet :

Een leeg record met uniek accountnummer wordt aangemaakt : spCreateAccount && sfMakeAccountNumber

Met een form worden andere gegevens ingevoerd en bewaard met spUserInsert

Procedure spGetDiagnostics doet voor als een uniek record al bestaat.

Andere procedures laten de lege records zien en wissen die.

Maak eerst een lege database aan ( upsert , wordt in connect.php gebruikt ) en voer dan onderstaand SQL bestand uit dat een tabel en de procedures en functie toevoegt.

Aan het eind worden die uitgevoerd.

Fouten voorbehouden.

Zelf ideeën toevoegen en/of uitwerken.

-- Adminer 4.8.1 MySQL 10.6.11-MariaDB-0ubuntu0.22.04.1 dump

SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';

SET NAMES utf8mb4;

DELIMITER ;;

DROP FUNCTION IF EXISTS `sfMakeAccountNumber`;;
CREATE FUNCTION `sfMakeAccountNumber`() RETURNS char(15) CHARSET utf8mb4 COLLATE utf8mb4_general_ci
BEGIN
SET @modulo = 26;
SET @nu     = NOW();
SET @jaar   = DATE_FORMAT( @nu, '%y' ) - 20;
SET @maand  = MONTH(@nu);
SET @dag    = MOD( DAYOFMONTH( @nu ), @modulo );

IF @dag = 0 THEN
	SET @dag = 1;
END IF;

#SET @wofm   = FLOOR( ( DAYOFMONTH( @nu ) - 1) / 7) + 1;

SET @uur = HOUR( @nu );
IF @uur = 0 THEN
	SET @uur = 1;
END IF;

SET @minuut = MOD( MINUTE( @nu ) , @modulo );
IF @minuut = 0 THEN
	SET @minuut = 1;
END IF;

SET @seconde = MOD( SECOND( @nu ) , @modulo );
IF @seconde = 0 THEN
	SET @seconde = 26;
END IF;

SET @micro = MICROSECOND( SYSDATE(4) );
IF @micro = '' THEN
	SET @micro = '9999';
END IF;
SET @micro = LPAD( @micro, 4, 0 );

SET @base     = 64;
SET @cjaar    = CHAR( @base + @jaar );
SET @cmaand   = CHAR( @base + @maand );
SET @cdag     = CHAR( @base + @dag );
#SET @cwofm    = CHAR( @base + @wofm );
SET @cuur     = CHAR( @base + @uur );
SET @cminuut  = CHAR( @base + @minuut );
SET @cseconde = CHAR( @base + @seconde );
SET @account  = CONCAT ( @cjaar, @cmaand, @cdag, @cuur, @cminuut, @cseconde, @micro, 'Z' );

RETURN @account;
END;;

DROP PROCEDURE IF EXISTS `spCheckEmptyAccounts`;;
CREATE PROCEDURE `spCheckEmptyAccounts`()
SELECT *
FROM users
WHERE naam IS NULL
ORDER BY registratiedatum;;

DROP PROCEDURE IF EXISTS `spCreateAccount`;;
CREATE PROCEDURE `spCreateAccount`()
BEGIN
	SET @errno = 0;
	REPEAT
		SET @account = sfMakeAccountNumber();
		INSERT IGNORE INTO users (account, registratiedatum, naam, adres, woonplaats) VALUES ( @account, NOW(), NULL, NULL, NULL);
		GET DIAGNOSTICS @num_conditions = NUMBER;
		SET @nummer = @num_conditions;
		GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;
	UNTIL @errno = 0 END REPEAT;
	SELECT @account AS account;
END;;

DROP PROCEDURE IF EXISTS `spDeleteEmptyAccounts`;;
CREATE PROCEDURE `spDeleteEmptyAccounts`()
DELETE FROM users WHERE naam IS NULL;;

DROP PROCEDURE IF EXISTS `spGetDiagnostics`;;
CREATE PROCEDURE `spGetDiagnostics`()
BEGIN
INSERT IGNORE INTO users (account, registratiedatum, naam, adres, woonplaats) VALUES ( 'BBFAUHJ28851K', NOW(), NULL, NULL, NULL);
GET DIAGNOSTICS @num_conditions = NUMBER;
SET @nummer = @num_conditions;
GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;
If @errno = 1062 THEN
  SELECT @errno AS fout;
ELSE
  SELECT @nummer;
END IF;
END;;

DROP PROCEDURE IF EXISTS `spUserInsert`;;
CREATE PROCEDURE `spUserInsert`(IN `faccount` char(15), IN `fnaam` char(50), IN `fadres` char(50), IN `fwoonplaats` char(50))
BEGIN

INSERT INTO
users ( account, registratiedatum, naam, adres, woonplaats )
VALUES ( faccount, now(), fnaam, fadres, fwoonplaats )
ON DUPLICATE KEY UPDATE 
	naam = values(naam),
	adres = values(adres),
	woonplaats = values(woonplaats);
END;;

DELIMITER ;

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account` char(15) NOT NULL,
  `registratiedatum` char(20) NOT NULL,
  `naam` char(50) DEFAULT NULL,
  `adres` char(50) DEFAULT NULL,
  `woonplaats` char(50) DEFAULT NULL,
  PRIMARY KEY (`account`),
  UNIQUE KEY `id` (`id`),
  KEY `naam` (`naam`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


-- 2022-12-27 09:48:04

# demo how it works
TRUNCATE TABLE users;
CALL spGetDiagnostics();
CALL spCheckEmptyAccounts();
CALL spGetDiagnostics();
CALL spCreateAccount();
CALL spCheckEmptyAccounts();
SELECT @account;
SET @naam = 'Fred';
SET @adres = 'Plein 54';
SET @woonplaats = 'Ergens';
CALL spUserInsert(@account, @naam, @adres, @woonplaats);
SELECT * FROM users WHERE account = @account;
SELECT * FROM users;
CALL spDeleteEmptyAccounts();
SELECT * FROM users;


Dit zijn de PHP bestanden.

connect.php om te verbinden

<?php
$SQL_host     = 'localhost';
$SQL_user     = 'root';
$SQL_password = 'password';
$SQL_port     = '3306';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db = new MySQLi( $SQL_host, $SQL_user, $SQL_password, '', $SQL_port );
$db->set_charset("utf8mb4");
$db->query("SET NAMES utf8mb4 COLLATE utf8mb4_general_ci");

$dbase = 'upsert';
$db->select_db( $dbase );
?>


menu.php

<style>
#menu {
	font-family: Copperplate;
	font-size: 1.200em;
	}

#menu a {
	text-decoration: none;
	}

</style>

<p id="menu">
<a href="UserInput.php">Input</a> |
<a href="UserUpdate.php">Update</a> |
<a href="UserShowAll.php">All</a>
</p>

<hr />


index.php om te beginnen

<?php
include('menu.php');
?>

<h3>Upsert Demo</h3>


UserInput.php om in te voeren

<?php

include('menu.php');

include ('connect.php'); // = new mysqli();
$query = "CALL spCreateAccount();";
$result = $db->query( $query );
if ( $result->num_rows > 0 ):
	$row = $result->fetch_object();
	echo '<pre>' . print_r( $row, TRUE ) . '</pre>';
	$account = $row->account;
endif;
?>

<style>
p, input {
	font-size: 120%;
}
</style>

<form method="post" action="UserPost.php">

	<p>Account <input type="text" name="account" value="<?php echo $account; ?>" readonly /></p>

	<p>Naam <input type="text" name="naam" value="" required /></p>

	<p>Adres <input type="text" name="adres" value="" required /></p>

	<p>Woonplaats <input type="text" name="woonplaats" value="" required /></p>

	<p><input type="submit" name="submit" value="submit" /></p>

</form>


UserPost.php om te verwerken en het resultaat te zien

<?php
include('menu.php');

echo '<pre>' . print_r( $_POST, TRUE ) . '</pre>';

if ( $_POST['submit'] ):

	include('connect.php');// = new mysqli();

	$faccount    = $db->real_escape_string( trim( $_POST['account'] ) );
	$fnaam       = $db->real_escape_string( trim( $_POST['naam'] ) );
	$fadres      = $db->real_escape_string( trim( $_POST['adres'] ) );
	$fwoonplaats = $db->real_escape_string( trim( $_POST['woonplaats'] ) );

	if ( !empty($faccount) && !empty($fnaam) && !empty($fadres) && !empty($fwoonplaats) ):
		$query = 'CALL spUserInsert("' . $faccount . '", "' . $fnaam . '", "' . $fadres . '", "' . $fwoonplaats . '")';
		echo '<p>' . $query . '</p>';

		$result = $db->query( $query );

		$query = 'SELECT * FROM users WHERE account = "' . $faccount . '"';
		$result = $db->query( $query );
		$row = $result->fetch_object();
		echo '<pre>' . print_r( $row , TRUE ) . '</pre>';
	else:
		echo '<p style="color:red;">Er ontbreken gegevens</p>';
	endif;

endif;
?>


UserUpdate.php om te bewerken

<?php
include('menu.php');
?>

<style>
	p, input {
		font-size: 120%;
	}
	</style>

<?php
if ( isset( $_GET['account'] ) ):
	
	include ('connect.php'); // = new mysqli();
	$account = $_GET['account'];
	$query = 'SELECT * FROM users WHERE account = "' . $account . '";';
	echo '<p>' . $query . '</p>';
	$result = $db->query( $query );
	if ( $result->num_rows > 0 ):
		$row = $result->fetch_object();
		echo '<pre>' . print_r( $row, TRUE ) . '</pre>';
		$account = $row->account;
		$naam = $row->naam;
		$adres = $row->adres;
		$woonplaats = $row->woonplaats;
		?>

		<form method="post" action="UserPost.php">

		<p>Account <input type="text" name="account" value="<?php echo $account; ?>" required /></p>

		<p>Naam <input type="text" name="naam" value="<?php echo $naam; ?>" required /></p>

		<p>Adres <input type="text" name="adres" value="<?php echo $adres; ?>" required /></p>

		<p>Woonplaats <input type="text" name="woonplaats" value="<?php echo $woonplaats; ?>" required /></p>

		<p><input type="submit" name="submit" value="submit" /></p>

		</form>

		<?php
	else:
		echo '<p>Account niet gevonden</p>';
	endif;
else:
	echo '<p>Accountnummer ontbreekt</p>';
endif;
?>
>> Om een uniek veld af te dwingen (bijv. user o.b.v. e-mail)

Als het gaat om het toevoegen van een username o.b.v. een e-mailadres, dan lijkt me inderdaad de eerste logische stap om te bepalen of die username wellicht al bestaat. Dit zou ik dan doen vóórdat ik nieuwe informatie aan de database ga toevoegen.

In het geval dat het mailadres (als username) al bestaat, heeft de eigenaar daarvan waarschijnlijk vergeten dat hij zich ooit al eens heeft ingeschreven. In dat geval kun je vragen: bent u uw wachtwoord vergeten? (en zo ja een wachtwoord reset-link mailen)
Simpel testen of iets bestaat :

https://www.tutorialspoint.com/best-way-to-test-if-a-row-exists-in-a-mysql-table

[size=xsmall]Toevoeging op 28/12/2022 20:35:53:[/size]

Dit kan ook :

DELIMITER ;;

DROP PROCEDURE IF EXISTS `spAccountBestaat`;;
CREATE PROCEDURE `spAccountBestaat`(IN `accountnum` char(15))
BEGIN
SET @x = (SELECT 1 FROM users WHERE account = accountnum);
#SELECT @x ;
SELECT COALESCE(@x, 0) AS bestaat;
END;;

DELIMITER ;


Een stored procedure die een 1 of 0 teruggeeft als een unieke gebruikersnaam wel of niet bestaat.

Je kan bij het opslaan vooraf een CALL spAccountBestaat() maken en daarop reageren.

Of je kunt bij het invoeren van de unieke gebruikersnaam bij elke keyup met AJAX e.e.a. dynamisch controleren.

https://www.w3schools.com/php/php_ajax_livesearch.asp

Reageren