PHP mysqli_prepare függvény használata több sor beszúrásához
2022-01-25T02:12:55+01:00
2022-01-27T08:11:34+01:00
2022-08-12T07:00:29+02:00
*deleted_23419333
Sziasztok!

Szeretnék beszúrni 15 sort 2 táblába, de csak akkor ha mind a 15-öt betudom, tehát 2 verzió lehetséges:

1. mind a 15 sort betudom szúrni vagy
2. inkább egyet se.

Hogyan tudnám ezt megvalósítani?

A mysqli_multi_query függvény kevés ehhez? Néztem még a mysqli_prepare függvényt is, de ezt még nem használtam és nem tudom lehet-e több sort beszúrni egyszerre.

A legbiztosabb megoldást keresem.

Az adatok API-ból jönnek és sajnos láttam már csodát, így valószínűleg kapok majd olyan adatot amire még nem vagyok felkészülve.

Szóval vagy adjam hozzá a táblákhoz mind a 15 sort vagy inkább egyet se és kapjak hibaüzenetet, FALSE értéket.

A SQL kód:

INSERT INTO `profile` ( `uid`, `username` ) VALUES ( 2022, 'M. Aladár' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '1', '1' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '2', '2' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '3', '3' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '4', '4' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '5', '5' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '6', '6' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '7', '7' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '8', '8' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '9', '9' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '10', '10' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '11', '11' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '12', '12' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '13', '13' ); INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( 2022, '14', '14' );

Az első sor nem mindig adódik hozzá, tehát ha már van 2022-es felhasználó a profile táblában, akkor azt nem adja hozzá és csak 14 sor megy a profile_meta táblába.

A teljesség igénye nélkül:

foreach( array( 2022=> 'M. Aladár', 2023=> 'N. Aladár' ) as $uid=> $username ) { $sql = "INSERT INTO `profile` ( `uid`, `username` ) VALUES ( " . $uid . ", '" . $username . "' );"; // Ha van már ilyen felhasználó akkor ez a sor nincs. $sql.= "INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( " . $uid . ", '1', '1' );"; }

Előre is köszönöm a segítséget.
Mutasd a teljes hozzászólást!
1. mind a 15 sort betudom szúrni vagy
2. inkább egyet se.

Tranzakciók használata? Ha valami menet közben nem klappol, akkor rollback, és a tranzakcióban korábban végrehajtott dolgok sem fognak érvényesülni.
Mutasd a teljes hozzászólást!

  • Köszönöm, hogy írtál. Így gondoltad?

    mysqli_begin_transaction( $mysqli ); try { mysqli_query( $mysqli, "INSERT INTO `profile` ( `uid` ) VALUES ( " . $uid . " )" ); mysqli_query( $mysqli, "INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( " . $uid . ", 'a', '1' )" ); mysqli_query( $mysqli, "INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( " . $uid . ", 'b', '2' )" ); mysqli_query( $mysqli, "INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES ( " . $uid . ", 'c', '3' )" ); } catch( mysqli_sql_exception $exception ) { mysqli_rollback( $mysqli ); throw $exception; }

    ... vagy a mysqli_query függvények helyett használnom kellene a mysqli_prepare, mysqli_stmt_bind_param, mysqli_stmt_execute és mysqli_commit függvényeket?
    Mutasd a teljes hozzászólást!


  • Ebben a részben egyébként mi történik?

    catch( mysqli_sql_exception $exception ) { mysqli_rollback( $mysqli ); throw $exception; }
    Mutasd a teljes hozzászólást!
  • vagy a mysqli_query függvények helyett használnom kellene a mysqli_prepare, mysqli_stmt_bind_param, mysqli_stmt_execute és mysqli_commit függvényeket?

    Nem tudom, mert a PHP-hoz nem értek. Úgy általánosságban mondtam hogy az atomi műveleteket tranzakciókkal szokás megvalósítani. Hogy a mysqli_* függvények mivel, hogyan jelzik a hibát, azt nem tudom, de van egy olyan gyanúm hogy a dokumentáció leírja

    Ebben a részben egyébként mi történik?

    Én tudjam, hiszen te írtad, nem? Ránézésre a mysqli_sql_exception típusú hiba dobódása esetén futtat egy rollbacket, aztán továbbdobja a hibát.
    Mutasd a teljes hozzászólást!
  • $mysqli->begin_transaction(); try { $uid = 2022; $username = 'M. Aladár'; $stmt = $mysqli->prepare('INSERT INTO `profile` ( `uid`, `username` ) VALUES (?,?)'); $stmt->bind_param('is', $uid, $username); $stmt->execute(); $uid = 2022; $meta_key = '1'; $meta_value = '1'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '2'; $meta_value = '2'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '3'; $meta_value = '3'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '4'; $meta_value = '4'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '5'; $meta_value = '5'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '6'; $meta_value = '6'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '7'; $meta_value = '7'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '8'; $meta_value = '8'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '9'; $meta_value = '9'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '10'; $meta_value = '10'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '11'; $meta_value = '11'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '12'; $meta_value = '12'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '13'; $meta_value = '13'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $uid = 2022; $meta_key = '14'; $meta_value = '14'; $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $uid, $meta_key, $meta_value); $stmt->execute(); $mysqli->commit(); } catch (mysqli_sql_exception $exception) { $mysqli->rollback(); throw $exception; }
    Ezt azért ciklusba is lehetne foglalni :)

    A $mysqli->begin_transaction(); elindítja a tranzakciót, a $mysqli->commit(); lezárja. Ami a kettő között van az vagy lefut egyben, vagy sehogy, azaz hiba esetén visszatörli a már beírt adatokat.

    } catch (mysqli_sql_exception $exception) {
        $mysqli->rollback();
        throw $exception;
    }

    Kérdezted, hogy ez mit jelent. Ez a PHP-ban a hibakezelés. Ami a catch ágban van, az hiba esetén fut le. Ide kerül a $mysqli->rollback(); ez vonja vissza gyakorlatilag az adatbázisműveletet.

    Arra figyelj még, hogy a tábla típusa InnoDB legyen, a MyISAM engin nem támogatja a tranzakciót. Ezt a phpmyadmin-ban ellenőrizheted.
    Mutasd a teljes hozzászólást!
  • Köszönöm a segítséget! Jól értem, hogy ez így működni fog?

    foreach() { $mysqli-> begin_transaction(); try { foreach() { $stmt = $mysqli-> prepare(); $stmt-> bind_param(); $stmt-> execute(); } $mysqli-> commit(); } catch (mysqli_sql_exception $exception) { $mysqli-> rollback(); throw $exception; } }
    Mutasd a teljes hozzászólást!
  • Most nincs előttem, hogy a mysqli cuccok miféle exception-öket dobnak, de ha jól emlékszem, akkor az execute() vissza tud térni simán false-szal is, ami szintén azt jelenti, hogy valami nem volt kerek... tehát erre is figyelni kellene.

    Megoldás lehet, hogy false esetén dobsz "kézzel" egy (megfelelő) exception-t, ekkor elég lehet az az 1 catch, amid van. Van olyan is, hogy finally, azzal is lehetne játszani.

    Egyébként viszonylag könnyű tesztelni: pl. a 13. insert-et rontsd el szándékosan!
    Mutasd a teljes hozzászólást!
  • Szia!

    Nem egészen erre gondoltam. 

    Röviden: a try-catch egy blokk a php scripten belül. Normális esetben a try ágban lévő kód fut le és a catch ágban lévő soha nem fut le (átugorja a php). Ha viszont a try szakaszban hiba van, akkor a php a hiba pontjáról rögtön a catch-ra ugrik és futtatja ami benne van. Ezért van az, hogy elindítod a tranzakciót, futtatod az adatbázis-műveleteket a try ágban, és ha valamelyikkel valamilyen hiba van, akkor a catch ágban "visszavonod" az adatbázis-műveleteket. 

    A catch ág ezzel a kifejezéssel indul:
    mysqli_sql_exception $exception
    Ez azt jelenti, hogy csak a mysqli kivételek esetén lép be a php a catch ágba, a többi hiba esetén nem. Többféle hiba van a PHP-ban, lehet egyedieket is készíteni, de itt konkrétan a mysql hibákra van szűkítve a hibakezelés. Az $exception azt jelenit, hogy ebbe a változóba rakta bele a hiba paramétereit a php. Az $exception->getMessage() megmutatja szövegesen hogy mi a hiba. Az $exception->getLine() megmutatja hogy melyik sorban van a hiba. És még tartalmaz egy-két infót az $exception változó, de talán ez a kettő a legfontosabb, ezeket a fejlesztés során echozhatod is (de az éles rendszerben adatbázishibák soha nem kerülhetnek a képernyőre!)

    A try ágban nem csak adatbázis-írás lehet, validálhatod is az adatokat, pl. érkezett-e adat, értéke nem kisebb vagy nagyobb egy meghatározott értéknél, stb. De ez csak egy példa, az a lényeg hogy nem csak adatbázis-függvények lehetnek a try ágban. (Amúgy validálni célszerű lenne az összes adatot egyben, még a tranzakció megindítása előtt, de ez már programszervezési kérdés...)

    Ha ciklusba teszed a tranzakció elindítását, lefuttatsz egy query-t és lezárod a tranzakciót, azzal nem éred el azt amit eredetileg szeretnél. Hiba esetén az az egy query kerül visszavonásra, ami a tranzakción belül van. A ciklust azért javasoltam, mert azonos query-ket futtatsz és hasonló adatokat írsz be az adatbázisba. De ezt csak akkor tudod megcsinálni, illetve akkor van értelme, ha valóban erről van szó, és nem csak példaadatokat mutattál a kérdésben. Ilyen ciklusra gondoltam:

    try { if (!$mysqli->begin_transaction()) { throw new Exception('Hiba! A tranzakciót nem sikerült elindítani.'); } $uid = 2022; $username = 'M. Aladár'; $stmt = $mysqli->prepare('INSERT INTO `profile` ( `uid`, `username` ) VALUES (?,?)'); $stmt->bind_param('is', $uid, $username); $stmt->execute(); $data = [ ['uid' => 2022, 'meta_key' => '1', 'meta_value' => '1'], ['uid' => 2022, 'meta_key' => '2', 'meta_value' => '2'], ['uid' => 2022, 'meta_key' => '3', 'meta_value' => '3'], ['uid' => 2022, 'meta_key' => '4', 'meta_value' => '4'], ['uid' => 2022, 'meta_key' => '5', 'meta_value' => '5'], ['uid' => 2022, 'meta_key' => '6', 'meta_value' => '6'], ['uid' => 2022, 'meta_key' => '7', 'meta_value' => '7'], ['uid' => 2022, 'meta_key' => '8', 'meta_value' => '8'], ['uid' => 2022, 'meta_key' => '9', 'meta_value' => '9'], ['uid' => 2022, 'meta_key' => '10', 'meta_value' => '10'], ['uid' => 2022, 'meta_key' => '11', 'meta_value' => '11'], ['uid' => 2022, 'meta_key' => '12', 'meta_value' => '12'], ['uid' => 2022, 'meta_key' => '13', 'meta_value' => '13'], ['uid' => 2022, 'meta_key' => '14', 'meta_value' => '14'], ]; foreach ($data as $item) { $stmt = $mysqli->prepare('INSERT INTO `profile_meta` ( `uid`, `meta_key`, `meta_value` ) VALUES (?,?,?)'); $stmt->bind_param('iss', $item['uid'], $item['meta_key'], $item['meta_value']); if (!$stmt->execute()) { throw new Exception('Hiba! A következő adatokat nem sikerül adatbázisba írni: ' . json_encode($item, JSON_UNESCAPED_UNICODE)); } } if (!$mysqli->commit()) { throw new Exception('Hiba! A tranzakciót nem sikerült lezárni.'); } } catch (Exception $exception) { // Visszavonja az adatbázisműveleteket $mysqli->rollback(); // Ezzel printelheted a hibaüzenetet echo $exception->getMessage(); // Ezzel továbbadhatod a hibát a függvényeden belül throw $exception; }

    Látod, sokkal rövidebb és talán áttekinthetőbb a kód. De ehhez az kell, hogy a $data tömbhöz hasonló adatszerkezeted legyen. Remélem nem kevertelek meg vele, ciklus nélkül is teljesen jó a program, nem kell erőltetni ha nem látod át teljesen.

    Ebben a példában alkalmaztam azt amit pookie2 javasolt és ellenőriztem a mysqli függvények visszatérési értékét, ami lehet true vagy false. Ha false, akkor egyedi szövegezésű hibát hozok létre, amit a catch ágban megfogok. Itt a catch ág már ezzel a feltétellel indul:
    Exception $exception
    Ez azt jelenti, hogy már nem csak a mysqli hibákat fogjuk meg, hanem az összeset (az Exception mindent magában foglal), így a saját hibaüzeneteinket láthatjuk hiba esetén viszont. Lehet így is kezelni a hibákat.

    Az első hozzászólásomban azt a hibát olvashatod az $exception->getMessage() echozásával, amit az adatbázis dob, és ez egy programozónak informatívabb. Ebben a foreach ciklusos példában saját hibaüzenetet olvashatsz, ez a felhasználónak informatívabb, Te viszont programozóként nehezen tudod kibogarászni hogy mi is volt a hiba valódi oka. Nem lehetetlen persze, de ahhoz egy kicsit többet kell kódolni. A legjobb gyakorlat az, ha csak a mysql hibákat fogod meg, ezt logolod, majd készítesz egy saját hibaüzenetet, hogy az "Adatbázis írás ismeretlen hiba miatt nem sikerült" és ezt adod tovább a böngészőnek. Ilyenkor az user látja hogy nem jött össze a dolog, Te pedig utólag meg tudod nézni a logban, hogy mi volt a hiba.

    Az első javaslatom a php.net ajánlása alapján készült, ott nem javasolják a mysqli függvények visszatérési értékének ellenőrzését. Ezért úgy gondolom, hogy erre csak akkor van szükség ha saját hibaüzenetekkel egészítenéd ki a kódodat.
    PHP: mysqli::begin_transaction - Manual

    Tesztelni a tranzakció megszakítását pedig ilyesmivel tudod (ezt a foreach ciklusba helyezd el):

    if ($item['meta_key'] == '13') { throw new mysqli_sql_exception('Teszt hiba. Ha ezt látod, akkor az adatbázisban nem lehet egy új rekord sem!'); }
    Mutasd a teljes hozzászólást!
abcd