Avatar billede Hans1 Praktikant
09. juni 2012 - 17:35 Der er 34 kommentarer og
1 løsning

Prepared statement - Tjek

Hey

Hvordan får jeg PHP til at udskrive om der virkelig er indsat en række i databasen. Jeg har sat tabellens struktur til at have en primær kolonne så den ikke vil indsætte samme række ved udførsel.

Hvordan udfører jeg et tjek som sletter de rækker som ikke skal være der mere hvis jeg nu har den data som skal sammenlignes med  i en foreach.


if ($stmt = $mysqli->prepare('INSERT INTO `ad_hoc` (`id`, `subject`, `requester`, `priority`, `deadline`, `status`) VALUES (?, ?, ?, ?, ?, ?)')) {

    $stmt->bind_param('isssss', $cols->item(0)->nodeValue, $cols->item(1)->nodeValue, $cols->item(2)->nodeValue, $cols->item(3)->nodeValue, $cols->item(4)->nodeValue, $cols->item(5)->nodeValue);

    $stmt->execute();

    $stmt->close();

} else {
    /* Der er opstÃ¥et en fejl */
    echo 'Der opstod en fejl i erklæringen: ' . $mysqli->error;
}
Avatar billede olebole Juniormester
10. juni 2012 - 00:04 #1
<ole>

$stmt->execute();
$numAffectRows = $stmt->affected_rows;

echo 'Du påvirkede '.$numAffectRows.' rækker.';

/mvh
</bole>
Avatar billede olebole Juniormester
10. juni 2012 - 00:06 #2
Avatar billede Hans1 Praktikant
10. juni 2012 - 09:29 #3
Fedt! Findes der så også en funktion som sammenligner en foreach med databasens indhold?
Avatar billede Hans1 Praktikant
10. juni 2012 - 12:54 #4
Det må være noget med:

DELETE FROM `ad_hoc` WHERE NOT `id` = ?
Avatar billede olebole Juniormester
10. juni 2012 - 20:14 #5
Forstår du mon selv, hvad du skriver?  =)
Avatar billede Hans1 Praktikant
10. juni 2012 - 20:54 #6
Egentlig ikke :(
Avatar billede olebole Juniormester
10. juni 2012 - 21:03 #7
Hvordan kommer vi så videre fra #2?  =)
Avatar billede Hans1 Praktikant
10. juni 2012 - 22:53 #8
Sagen er den at jeg via noget XML DOM med foreach henter noget data fra en HTML tabel fra en anden side. I første omgang tilføjer jeg alle disse rækker til databasen (koden i post #0 + #1)

Nu er problemet så bare at der i databasen skal slettes de rækker som ikke længere eksisterer i HTML tabellen.
Avatar billede olebole Juniormester
10. juni 2012 - 23:18 #9
Okay, én ting ad gangen: En af fordelene ved at bruge prepared statements er, at det oprettede statement 'compiles' af databasen. Derefter kan det samme statement eksekveres så mange gange, det måtte ønskes - med nye værdier for hver eksekvering.

Jeg har lidt på fornemmelsen, du opretter og lukker dit statement inde i en løkke. Prøv denne fremgangsmåde i stedet:

if ($stmt = $mysqli->prepare('INSERT INTO `ad_hoc` (`id`, `subject`, `requester`, `priority`, `deadline`, `status`) VALUES (?, ?, ?, ?, ?, ?)')) {

    [LOOP_BEGIN]
    $stmt->bind_param('isssss', $cols->item(0)->nodeValue, $cols->item(1)->nodeValue, $cols->item(2)->nodeValue, $cols->item(3)->nodeValue, $cols->item(4)->nodeValue, $cols->item(5)->nodeValue);
    $stmt->execute();
    [LOOP_END]

    $stmt->close();

} else {
    /* Der er opstået en fejl */
    echo 'Der opstod en fejl i erklæringen: ' . $mysqli->error;
}

Jeg kender ikke din løkkekode, så jeg har bare angivet loopet med pseudokode.

Hvis der er tale om 10-20-50-100 rækker, så slet alle rækker og indsæt dem på ny. Med prepared statements går det lynhurtigt
Avatar billede olebole Juniormester
10. juni 2012 - 23:51 #10
@Stefan1: Når vi tale MySQLI, er det meget ofte i forbindelse med sikkerhed. Der er er dog en anden meget væsentlig begrundelse for at bruge prepared statements, og der er #9 et skoleeksempel.

Når du kalder MySQLI-klassens prepare metode, parser denne den streng, du sender med. Du kan derefter opfatte det som, at databasen opretter en funktion, der udfører det, strengen siger, når funktionen senere bliver kaldt.

Med det returnerede statements bind_param og execute metoder kalder du funktionen. Det er bare et kald, som er opdet i to faser: Først angives argumenterne (parametrene) og derefter kaldes (eksekveres) funktionen.

Det fungerer lidt på samme måde, som når du i PHP skriver en funktion og kalder den mange gange med forskellige argumenter.

I det gamle MySQL-API skulle databasen 'bygge hele maskinen' hvergang et kald blev udført.

Intet er dog helt gratis, og MySQLI er faktisk marginalt langsommere på enkeltkald - men altså dramtisk hurtigere på repeterende kald.

Du bør altså som hovedregel oprette et statement og vente med at lukke det, til du ikke længere har brug for det indenfor samme funktion. Du kan sagtens oprette flere - blot giv dem forskellige navne. Ligesom du ville gøre det med almindelige funktioner/objekter
Avatar billede Hans1 Praktikant
11. juni 2012 - 08:06 #11
@olebole: Du havde ret i at jeg oprettede og lukkede mit statement inde i en løkke. Jeg har nu rettet det i forhold til #9 - Rigtig god forklaring - Jeg lærte helt sikkert noget nyt :o
Avatar billede Hans1 Praktikant
11. juni 2012 - 18:05 #12
Forstod du hvad det var jeg mente i #8?
Avatar billede olebole Juniormester
11. juni 2012 - 18:17 #13
Det tror jeg - og i så fald var svaret: "Hvis der er tale om 10-20-50-100 rækker, så slet alle rækker og indsæt dem på ny. Med prepared statements går det lynhurtigt"  =)

Er der tale om forholdsvis få rækker, kan det hurtigt blive mindre effektivt at prøve at slette enkeltrækker
Avatar billede Hans1 Praktikant
11. juni 2012 - 18:33 #14
#13 Jeg vil helst gøre det sådan at den kun udskriver de nye som er kommet til.
Via koden:


$stmt->execute();
$numAffectRows = $stmt->affected_rows;

echo 'Du påvirkede '.$numAffectRows.' rækker.';


Hvis jeg sletter alle rækker så vil den indsætte rækker som allerede har været i databasen før sletningen. Hvis jeg nu sendte en mail ud via php hver gang der blev indsat en ny række ville man få tilsendt en mail med med de samme rækker da den bare indsætter de samme i databasen.
Avatar billede olebole Juniormester
12. juni 2012 - 14:05 #15
Okay, nu forstår jeg. Jeg havde vist misforstået projektet lidt fra start  =)

Jeg ville gå en anden vej. Jeg ville kun indsætte de rækker, der ikke allerede er i databasen - og derefter sende mails ud om dem, der blev indsat.

Det kræver, der i hver række står noget i et af dine XML-items, som er unikt for lige netop dén række. Så vil man nemlig kunne udnytte, at MySQL kaster en fejl 1062, hvis man har et unikt felt i databasen - og man prøver at indsætte noget, der står i forvejen.

Da jeg ikke kender dine data, får du her en mere generisk løsning: Opret et ekstra felt i databasen. Det skal være af typen VARCHAR - have en max-længde på 32 - og sættes til at være unik.

Så kan du prøve med følgende kode:

$arrInfo4Mails = array();
if ($stmt = $mysqli->prepare('INSERT INTO `ad_hoc` (`id`, `subject`, `requester`, `priority`, `deadline`, `status`, `hash`) VALUES (?, ?, ?, ?, ?, ?, ?)')) {

    [LOOP_BEGIN]
    $hash = md5($cols->item(0)->nodeValue.$cols->item(1)->nodeValue.$cols->item(2)->nodeValue.$cols->item(3)->nodeValue.$cols->item(4)->nodeValue.$cols->item(5)->nodeValue);
    $stmt->bind_param('issssss', $cols->item(0)->nodeValue, $cols->item(1)->nodeValue, $cols->item(2)->nodeValue, $cols->item(3)->nodeValue, $cols->item(4)->nodeValue, $cols->item(5)->nodeValue, $hash);
    @$stmt->execute();
    if ($stmt->errno!=1062) {
        if ($stmt->errno!=0) {
            die('Der opstod en fejl, som ikke skyldes "duplicate entry". Tjek og ret.');
        }
        $arrInfo4Mails[] = array($stmt->insert_id, $cols->item(0)->nodeValue, $cols->item(1)->nodeValue, $cols->item(2)->nodeValue, $cols->item(3)->nodeValue, $cols->item(4)->nodeValue, $cols->item(5)->nodeValue);
    }
    [LOOP_END]

    $stmt->close();
} else {
    /* Der er opstået en fejl */
    echo 'Der opstod en fejl i erklæringen: ' . $mysqli->error;
}
$mysqli->close();

if (count($arrInfo4Mails)>0) {
    // Send mails her
    var_dump($arrInfo4Mails);
} else {
    echo 'Ingen nye rækker.';
}

Jeg danner en md5-hash af alle felternes samlede indhold og forsøger at sætte den ind sammen med de andre data. Hvis denne hash findes i forvejen, vil execute smide en fejl. Den undertrykker vi dog med et @ foran kaldet.

Opstår der en fejl 1062, gør vi intet. Gør der ikke det, tjekker vi for andre fejl. Opstod der ikke andre fejl, hælder vi data ned i array'et $arrInfo4Mails - som til sidst kan bruges til mail-udsendeing.

Ved koden:

    // Send mails her
    var_dump($arrInfo4Mails);

- kan du selv afgøre, hvilke data du vil have med - og efterfølgende rette linjen, hvor de fyldes i array'et.

Det burde virke  =)
Avatar billede olebole Juniormester
12. juni 2012 - 14:07 #16
PS: Med min kode skal det nye felt hedde hash
Avatar billede Hans1 Praktikant
13. juni 2012 - 14:04 #17
Tak. Kigger på det.
Avatar billede olebole Juniormester
13. juni 2012 - 15:57 #18
Gør det ... og spørg endelig  =)
Avatar billede Hans1 Praktikant
13. juni 2012 - 16:53 #19
Jeg indsætter allerede de rækker som ikke findes i databasen.
Da Jeg har et unikt nummer i form af id (ikke auto increment) bliver dem som allerede findes i databasen ikke tilføjet igen. Det som er problemet er at hvis de data som skal indsættes i databasen ikke findes mere i dom objektet skal det fjernes fra databasen. Indholdet fra Dom dokumentet skal simpelthen være identisk med databasen.
Avatar billede olebole Juniormester
13. juni 2012 - 17:29 #20
Okay, så havde jeg faktisk forstået det korrekt i første omgang!  :D

Så er vi tilbage i at slette alle rækker først - og derefter indsætte DOM-objektets rækker:

$mysqli->query('DELETE FROM `ad_hoc`');

- og derefter kører du koden i #9.

Det kræver flere resourcer at sammenligne DOM-objektet og databasen for at slette rækker, der ikke spejler DOM'en. Når du opretter et prepared statement og genbruger det til flere/mange indsættelser, går det rasende hurtigt. Du kan slet ikke sammenligne det med det gamle API ved gentagne kald
Avatar billede Hans1 Praktikant
13. juni 2012 - 20:43 #21
Jeg tror stadig ikke at jeg helt har forklaret mig korrekt.
Jeg kan godt se idéen med at slette alle rækker og derefter indsætte dem via DOM-objektet - Så er det opdateret - det er rigtigt.

Men Problemet er jo så bare at jeg via mail sender en mail pr række som er indsat.
Det vil den så gøre for hver gang at den har kørt DELETE FROM forespørgslen.

En rækkes indsættelse må kun sendes én gang via mail. Så hvis den slette via DELETE FROM og derefter indsættes igen fordi at den stadig findes i DOM-objektet så vil den jo sende mail en gang til fordi at den er indsat og det må den ikke.
Avatar billede olebole Juniormester
13. juni 2012 - 21:32 #22
forstår jeg ... igen!  :D

Mon ikke, det er sådan noget lignende, du så efterspørger?

$arrInDB = array();
$arrData4mails = array();

$res = $mysqli->query('SELECT `id` FROM `ad_hoc`');
while ($row=$res->fetch_assoc()) {
    $arrInDB[$row['id']] = 1;
}
$res->close();

$mysqli->query('DELETE FROM `ad_hoc`');

if ($stmt = $mysqli->prepare('INSERT INTO `ad_hoc` (`id`, `subject`, `requester`, `priority`, `deadline`, `status`) VALUES (?, ?, ?, ?, ?, ?)')) {

    [LOOP_BEGIN]
    $stmt->bind_param('isssss', $cols->item(0)->nodeValue, $cols->item(1)->nodeValue, $cols->item(2)->nodeValue, $cols->item(3)->nodeValue, $cols->item(4)->nodeValue, $cols->item(5)->nodeValue);
    $stmt->execute();
    if (!$arrInDB[$cols->item(0)->nodeValue]) {
        $arrData4mails[] = array($cols->item(0)->nodeValue, $cols->item(1)->nodeValue, $cols->item(2)->nodeValue, $cols->item(3)->nodeValue, $cols->item(4)->nodeValue, $cols->item(5)->nodeValue);
    }
    [LOOP_END]

    $stmt->close();

} else {
    /* Der er opstået en fejl */
    echo 'Der opstod en fejl i erklæringen: ' . $mysqli->error;
}

if (count($arrData4mails)>0) {
    var_dump($arrData4mails);
} else {
    echo 'Ingen nye data.';
}
Avatar billede olebole Juniormester
13. juni 2012 - 21:33 #23
Det forudsætter, jeg har gættet rigtigt, og dit id ligger i item(0)  =)
Avatar billede olebole Juniormester
13. juni 2012 - 21:36 #24
Dohhhh ... det står der jo i SQL'en, Ole!
Avatar billede Hans1 Praktikant
13. juni 2012 - 22:08 #25
Hehe Ole.

Efter at jeg har sat det sammen får jeg: unexpected T_STRING


$arrInDB[$row['id']] = 1;
|

Kan det have noget at gøre med at id'et i DOM'en går fra ca 34079?
Avatar billede olebole Juniormester
13. juni 2012 - 22:47 #26
Nej, det burde ikke betyde noget. Gad vide, hvad der står i det felt, hvor den fejler? Prøv lige:

while ($row=$res->fetch_assoc()) {
    echo 'Index: '.$row['id'].'<br>';
    $arrInDB[$row['id']] = 1;
}
Avatar billede Hans1 Praktikant
14. juni 2012 - 08:53 #27
Sorry havde glemt at tilføje indholdet til din $res variabel :s

Nu viser den alt indhold nederst i et array men jeg får også denne fejl øverst:
Undefined index: 34453  on line 73  <---- 34453 er id'et for rækken

Dette viser den hele vejen ned på siden med hvert unikt id.


Her er linie 73:

if (!$arrInDB[$cols->item(0)->nodeValue]) {
Avatar billede olebole Juniormester
14. juni 2012 - 14:09 #28
Årrhhh ... det er mig, der er en torsk! Skift den linje ud med:

if (!isset($arrInDB[$cols->item(0)->nodeValue])) {

- sorry  =)
Avatar billede Hans1 Praktikant
14. juni 2012 - 14:41 #29
Ole. Du er genial :D

Tusind tak.
Avatar billede olebole Juniormester
14. juni 2012 - 15:16 #30
Selvtak. Gammel, bad practice slår desværre af og til igennem hos ældre mænd, når de ikke tester deres kode. Det er sgu'tte så genialt  :D

Du lægger som vanligt selv et svar og accepterer, så tråden lukkes  =)
Avatar billede Hans1 Praktikant
16. juni 2012 - 20:23 #31
Svar.
Avatar billede Hans1 Praktikant
18. juni 2012 - 11:46 #32
Er det muligt at lave en foreach med arrayet: $arrData4mails i bunden?
Avatar billede olebole Juniormester
18. juni 2012 - 14:32 #33
Ja, det kan du altid med et array:

foreach ($arrData4mails as $key => $val) {
    echo $key . ' => ' . $val . '<br>';
}
Avatar billede Hans1 Praktikant
18. juni 2012 - 17:15 #34
Ved hjælp af dit eksempel får jeg det udkrevet således:

0 => Array
1 => Array
2 => Array

osv.


Men jeg kunne også godt bruge selve værdierne som befinder sig selve array'et


Har prøvet med følgende kode taget inde fra php.net manualen der for jeg udskrevet værdien af første variabel men i den næste for jeg blot 1 og 0 taller.


foreach($arrData4mails as $obj)
{
    foreach( $obj as $key => $value )
    {
      echo $key . ' - ' . $value;
    }
}


Hvordan er det lige jeg får $key som $cols->item(0)->nodeValue
samt $value som $cols->item(1)->nodeValue ?
Avatar billede Hans1 Praktikant
18. juni 2012 - 17:26 #35
Når. Sådan her :)

Tak.

foreach ($arrData4mails as $key) {
    echo $key[0] . ' => ' . $key[1] . '<br>';
}
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester