Inserare in baza de date duplicare

$vid  = 1000;
$title = 'test';

function checkvid($vid) {
    global $pdo;
    $stmt = $pdo->prepare("select 1 from lista where vid=?");
    $stmt->execute([$vid]); 
    return $stmt->fetchColumn();
}

if (!checkvid($vid)): 
      $sql = "insert into lista (title,vid) values (?,?)";
      $pdo->prepare($sql)->execute([$title, $vid]);
else:
      // exista deja...
endif;

Problema este că dacă rulez acest cod repetitiv în mai puțin de o secundă, să zic așa, item-ul se adaugă în bază de mai multe ori chiar dacă am acea verificare…

Modifica checkvid sa returneze true sau false. Daca este true, atunci video-ul exista. Cea mai simpla idee

Pardon my french, dar nu există constrângere pe acel vid să fie unic? Îți complică codul ceva ce ar putea foarte simplu implementat din db…

2 Likes

Text preformatat`Incearca cu rowCount() in loc de fetchColumn()
Am gresit, scuze, e selectare. Aloca o variabila pentru $stmt->fetchColumn() si apoi returneaz-o. Ai mai putea sa faci un var_dump($stmt->fetchColumn()) pentru debugging.
Am mai gasit si asta https://stackoverflow.com/questions/16498640/pdo-prepare-with-question-marks-doesnt-work-with-numbers
Daca e din cauza asta atunci foloseste

function checkvid($vid) {
    global $pdo;
    $stmt = $pdo->prepare("select 1 from lista where vid=:vid");
    $stmt->bindParam(':vid', $vid);
    $stmt->execute(); 
    return $stmt->fetchColumn();
}

Daca nu ai logica pentru cazul in care item-ul exista poti sa faci in felul urmator:

  1. Modifici tabela ca sa aiba un uniq constraint(fie vid, fie vid+title)
ALTER TABLE `lista` ADD UNIQUE `unique_index`(`title`, `vid`);
  1. Folosesti INSERT IGNORE
INSERT IGNORE INTO `lista` (`title`,`vid`) values (?,?);

Astfel poti sa scapi de functia checkvid.

1 Like

Această variantă e ok, dar dacă fac asta, cum mai pot returna în php atunci când item-ul deja există ?

Știu asta, mă refer că vreau să adaug un alt cod în caz că item-ul există în bază.
Renunț la funcția checkvid și rămân doar cu asta:

      $sql = "insert into lista (title,vid) values (?,?)";
      $pdo->prepare($sql)->execute([$title, $vid]);

Cum aș mai putea să pun un else să facă altceva?

Hai să punem altfel problema: ce vrei să faci mai exact din codul ăsta? Abordarea ta, din capul locului, s-ar putea să aibă nevoie de îmbunătățiri. Există o mulțime de variante prin care se evită duplicatele: constrângere pe vid, selectarea vid-ului maxim și apoi incrementarea lui cu 1 de fiecare data când inserezi ceva (care e de fapt logica constrainutului), sau chiar soluții mai complicate care merită folosite numai dacă arhitectura aplicației nu permite altfel.

Nu am înțeles nici faza cu o secundă. În mai mult de o secundă nu îți creează duplicate? De unde ai luat acea secundă?

Plecand de la codul tau intial poti sa verifici cate randuri returneaza statement-ul tau.

$stmt->rowCount()
function vidAlreadyExists($vid) {
    global $pdo;
    $stmt = $pdo->prepare("select 1 from lista where vid=:vid");
    $stmt->bindParam(':vid', $vid);
    $stmt->execute(); 
    return $stmt->rowCount() != 0
}

if (!vidAlreadyExists($vid)): 
      $sql = "insert into lista (title,vid) values (?,?)";
      $pdo->prepare($sql)->execute([$title, $vid]);
else:
      // exista deja...
endif;

Recomand ca logica if/else sa fie incapsulata intr-o procedura stocata.

Din testele interne am performanta mult mai buna si consistenta doar aducand informatiile din baza de date (eventual punand GROUP BY/ORDER BY) si procesand in PHP decat lasand logica pe seama bazei de date.

2 Likes

Este ceva gresit aici. Daca exista deja de ce vrei sa ii dai altul? Daca incerci sa calculezi id-ul in php in general vei avea probleme. Fara un context nu avem cum sa oferim solutia.

PHP-ul nu poate garanta că un tabel nu va conține duplicate, oricâte verificări ai face. Ai nevoie, obligatoriu, de o coloană unică (ți-a răspuns Răzvan mai sus).

Iar răspunsul la ultima ta întrebare este “INSERT INTO table … ON DUPLICATE KEY UPDATE”. Vezi aici exemplu: https://stackoverflow.com/a/4205207/7551933

Edit: Poți să verifici cu php. Poți folosi “INSERT IGNORE” care va returna 1 dacă rândul a fost inserat și 0 dacă nu a fost inserat. Cam așa ar trebui să ai:

<?php
$vid  = 1000;
$title = 'test';

$sql = "insert ignore into lista (title,vid) values (?,?)";
$pdo->prepare($sql)->execute([$title, $vid]);
if($pdo->rowCount() === 1)
{
	// a fost inserat acum...
}
else
{
	// exista deja...
}
2 Likes