Avatar billede Morten Professor
28. marts 2021 - 21:56 Der er 30 kommentarer

Hvert 2 min kør 10 row ind af gangen i xml fil fra databasen

Hej Jeg har et spørgsmål jeg håber i har en løsning på.
Har lavet function til at ligge data på xml fil fra databasen, da det skal bruges i et feed.
Det jeg har problemer med at jeg højst han tage en måned a gangen og så bruger den for meget memory, har 256M at gøre med så skal have det til at spille.
Som for i kan se hvad der er lavet indtil videre er lige her:
$doc = new DOMDocument('1.0', 'UTF-8');
   
    $doc->formatOutput = true;

    $r = $doc->createElement( "items" );
    $doc->appendChild( $r );

    foreach( $feed_nyhedsbrev as $feed_nyhedsbrev_data )
    {
        $b = $doc->createElement( "item" );

        $meta_sku = $doc->createElement( "ExternalId" );
        $meta_sku->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["ExternalId"] )
        );
        $b->appendChild( $meta_sku );

        $post_title = $doc->createElement( "Name" );
        $post_title->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["Name"] )
        );
        $b->appendChild( $post_title );

        $imageLink = $doc->createElement( "ImageLink" );
        $imageLink->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["ImageLink"] )
        );
        $b->appendChild( $imageLink );

        $additionalImageLink = $doc->createElement( "AdditionalImageLink" );
        $additionalImageLink->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["AdditionalImageLink"] )
        );
        $b->appendChild( $additionalImageLink );

        $guid = $doc->createElement( "Link" );
        $guid->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["Link"] )
        );
        $b->appendChild( $guid );

        $koen = $doc->createElement( "Gender" );
        $koen->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["Gender"] )
        );
        $b->appendChild( $koen );

        $brand = $doc->createElement( "Brand" );
        $brand->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["Brand"] )
        );
        $b->appendChild( $brand );

        $meta_stock = $doc->createElement( "InStock" );
        $meta_stock->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["InStock"] )
        );
        $b->appendChild( $meta_stock );

        $post_parent = $doc->createElement( "ItemGroupId" );
        $post_parent->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["ItemGroupId"] )
        );
        $b->appendChild( $post_parent );

        $meta_regular_price = $doc->createElement( "RegularPrice" );
        $meta_regular_price->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["RegularPrice"] )
        );
        $b->appendChild( $meta_regular_price );

        $meta_sale_price = $doc->createElement( "SalePrice" );
        $meta_sale_price->appendChild(
            $doc->createTextNode( $feed_nyhedsbrev_data["SalePrice"] )
        );
        $b->appendChild( $meta_sale_price );

        $r->appendChild( $b );
    }

    $doc->saveXML();
    $doc->save("nyhedsbrev.xml");

Det jeg har brug for at løse problemet er at tage lidt af gangen, og ikke overskrive det der er i xml filen, da det så ikke giver mening at have et cronjob kørende til den skal tage 10 row ind hvor den så aldrig kommer videre end de 10. Det skulle helst køre sådan 2 min = 10row - 2 min = 20row og så fortsætte der op af.
Har set andre gør det når de skal have en stor mængde data ud. så den ikke mangler memory.
Jeg ved bare ikke hvordan har søgt en del men finder ikke lige det jeg har brug for, ellers er jeg bare ikke så god til at lede det rigtige sted ;-)

Håber i vil kigge på det, og komme med nogle forslag til at løse det.

Med venlig hilsen
Morten
Avatar billede acore Ekspert
29. marts 2021 - 00:12 #1
Nu er en xml ikke så kompliceret. Kan du ikke bare åbne filen, og så skrive til den med fwrite()  i stedet for at bruge Domdocument?
Avatar billede arne_v Ekspert
29. marts 2021 - 02:31 #2
PHP har en XmlWriter klasse som er designet til at skrive meget store XML filer.

https://www.php.net/manual/en/book.xmlwriter.php
Avatar billede Morten Professor
29. marts 2021 - 07:02 #3
Det må jeg lige prøve, så kan jeg se om hvor mange måneder den kan gå tilbage.
Prøver lige XmlWriter
Tak for de forslag, jeg vender lige tilbage når jeg har prøvet det af.
Avatar billede Morten Professor
29. marts 2021 - 09:56 #4
Hej

Så prøvede jeg den siger Fatal error
: Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes
Har ikke mulighed for sætte memory op, da det er på en hosting service. og de har nogle regler som jeg er nød til at forholde mig til.
Er der nogen anden mulighed?
Avatar billede arne_v Ekspert
29. marts 2021 - 15:01 #5
Det er næppe XmlWriter som bruger den memory.

XmlWriter skriver direkte ud og gemmer ikke data i memory.

Der må være boget andet som bruger den memory.
Avatar billede acore Ekspert
29. marts 2021 - 15:38 #6
Kan du poste kdoen?

arne_v: Skriver XmlWriter altid ud til fil? At dømme efter dokumentationen, kan den også skrive til memory, som så kan tømmes med xmlwriter_output_memory(...)
Avatar billede Morten Professor
29. marts 2021 - 16:19 #7
Hej

Mit hent af data ser sådan ud:
$getJsonData = $wpdb->get_results($wpdb->prepare("SELECT DISTINCT ID, post_date, nw_posts.guid,  nw_posts.post_parent, nw_posts.post_title, nw_posts.post_type, nw_postmeta.post_id, nw_postmeta.meta_value, nw_postmeta.meta_key
    FROM nw_posts INNER JOIN nw_postmeta ON ID = nw_postmeta.post_id WHERE post_date BETWEEN %s AND %s", array($start, $stop)));
   
Den her post_date BETWEEN %s AND %s var for at løse antallet af produkter den skulle igennem.

Har omkring 6000 produkter som har ialt 27000 varianter.
Så der er en del der skal køres igennem den ene tabel nw_postmeta.post_id er varianterne nw_posts er master produkterne.

Håber det kan give nogen ide om hvad der sker
Avatar billede arne_v Ekspert
29. marts 2021 - 16:36 #8
#6

openUri skriver til fil (i en lidt bred forstand af fil) og openMemory skriver til memory.

Men eksistensen af outputMemory indikerer at den bruger en buffer, så der skal nok sættes nogle kald af flush ind.

Jeg troede faktisk at den gjorde det automatisk, men tilsyneladende ikke.

https://codeinthehole.com/tips/creating-large-xml-files-with-php/
Avatar billede arne_v Ekspert
29. marts 2021 - 16:37 #9
#7

Det er ikke så meget hvor de store data kommer fra men mere hvordan du bruger XmlWriter.

Men jævnfør #8 så skal der nok indsættes nogle flush kald på den XmlWriter.
Avatar billede Morten Professor
29. marts 2021 - 21:17 #10
Hej igen
Ja der må da være noget galt^
$CountProductsData = $wpdb->get_results($wpdb->prepare("SELECT post_type
    FROM nw_posts WHERE post_type = %s", array('product')));

    var_dump(count($CountProductsData));

Nu har jeg gjort simpelt og bare tjekke type og der kommer den også med den fejl:

Og det er uden at forsøge at ligge det i en fil.

Fatal error
: Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes) i
Avatar billede arne_v Ekspert
29. marts 2021 - 21:30 #11
Henter $wpdb->get_results alt fra databasen op i memory?

Hvis ja, så er det jo nok de rproblemegt ligger.

Med så store data skal der også bruges et streaming API til at hente fra databasen.
Avatar billede Morten Professor
29. marts 2021 - 21:48 #12
Ja det må, men da jeg nøjes med
$getXMLData = $wpdb->get_results("SELECT post_type FROM nw_posts WHERE post_type = 'product'");

    print_r(count($getXMLData));

Kan jeg få det rigtige tal frem af produkter.

Men har brug for at hente data fra den anden tabel for at få pris på varianter
Men uden Join kan jeg få mit rigtige tal frem af produkter
Avatar billede Morten Professor
29. marts 2021 - 21:49 #13
Kan jeg på en anden måde hente det ned? fra databasen?
Håber virkeligt der er en anden måde end at bruge  $wpdb->get_results
Avatar billede acore Ekspert
29. marts 2021 - 23:37 #14
Hvis du er usikker på hvad der forårsager fejlen, kan du lægge nogle sladreudskrifter ind.

Hvis problemet er at data hentes på en gang, kan du så ikke lave en løkke, hvor du henter x000 ad gangen med LIMIT x,y på din SQL?
Avatar billede Morten Professor
29. marts 2021 - 23:54 #15
Den der løkke kan jeg gøre så den automatisk tager næste ryk efter der er gået en pause
Avatar billede Morten Professor
30. marts 2021 - 00:20 #16
Har en anden løsning som måske vil virke hvis jeg ikke kan få det andet til at virke lave en table med de colums jeg skal bruge og hente det fra tabellerne Hvis det da ikke tager for mange resurcer så jeg står med det samme problem igen.
Avatar billede arne_v Ekspert
30. marts 2021 - 01:33 #17
Jeg har kigget lidt på det WP API.

https://developer.wordpress.org/reference/classes/wpdb/

Det er uegnet til store data.

Men på nettet foreslåes det at man laver en:

$con = $wpdb->__get('dbh');

og så bruger mysqli API med $con.

LIMIT ideen bør dog også virke.
Avatar billede Morten Professor
30. marts 2021 - 06:05 #18
Tak jeg vil lige prøve det af så vender jeg tilbage :-)
Avatar billede Morten Professor
30. marts 2021 - 11:02 #19
Jeg prøvede på det men syntes ikke rigtig jeg kunne få den til det.

Jeg er ved at lave et sted til oplysningerne hvor dan så ikke skal lede for meget når det skal ind i xml filen.

Men har et problem med min syntax:
global $wpdb;

    $sql = "INSERT INTO ***_nyheds_xml_feed (ID, post_date, guid, post_parent, post_title, post_type, **_postmeta.post_id, **_postmeta.meta_key, **_postmeta.meta_value)
SELECT ID, post_date, guid, post_parent, post_title, post_type, **_postmeta.post_id, **_postmeta.meta_key, **_postmeta.meta_value
FROM **_posts
    INNER JOIN **_postmeta ON ID = **_postmeta.post_id WHERE post_type = 'product'";

    $wpdb->query($sql);


Ved ikke om jeg gør det rigtigt får ikke fejl men der kommer ikke noget der ind i den nye table.
Avatar billede acore Ekspert
30. marts 2021 - 18:52 #20
Du skriver "syntes ikke rigtig jeg kunne få den til det". Det kan vi ikke bruge til at hjælpe dig.

Du har to løsningsforslag. Hvad gør du? Hvad sker der?

Det, du skriver i #19 giver ingen mening i forhold til de to veje, du har fået foreslået.
Avatar billede Morten Professor
30. marts 2021 - 19:43 #21
Hov undskyld kan jeg godt se i ikke får så meget ud af.
Prøvede $con = $wpdb->__get('dbh');
Hjalp ikke på det, samme problem stilling men kan være jeg havde gjort noget forkert.

Det med limit hvordan får jeg det til at loop igennem rigtigt så der kommer en pause imellem loopene kan godt se det vil være smart men lige at få den til det, ville være nød til at trykke run hver gang det var nået frem til de feks. 1000 og så offset 1001 til 2000,
Er der noget jeg kan gøre der for det ville være fedt. :)
Avatar billede acore Ekspert
30. marts 2021 - 20:00 #22
Hvorfor skal der være pause? Er dit problem ikke kun out of memory?

Hvordan forestiller du dig, at vi kan fortælle, hvad du gør forkert, når du ikke viser, hvad du gør? Vi er ikke orakler.
Avatar billede arne_v Ekspert
30. marts 2021 - 20:06 #23
$con = $wpdb->__get('dbh');

tillader dig at bruge normal mysqli.

    $con = $wpdb->__get('dbh');
    $stmt = $con->prepare('SELECT ...');
    $stmt->execute();
    $rs = $stmt->get_result();
    while($row = $rs->fetch_array(MYSQLI_ASSOC)) {
        // process $row
    }
    $stmt->close();
Avatar billede Morten Professor
30. marts 2021 - 20:21 #24
acore Jeg forsøger ikke at være uhøflig eller regner med i bare kan sige hvad der er galt.
Forsøger så godt jeg kan er ikke så god til at forklare.
Jeg syntes det er fedt i vil hjælpe mig.

Acore jeg troede der skulle være lidt pause og noget flush inden det næste loop derfor spurgte jeg til det.

arne_v Fedt man, det så det er på den måde jeg kan gøre det nu så. Det skal testes af. Den gode trofaste indstilling :D
Kunne næsten kun huske wordpress metoden.
Avatar billede acore Ekspert
30. marts 2021 - 20:48 #25
Det er ikke fordi, jeg synes du er uhøflig. Men det hjælper, at poste kode og vise hvilken respons du får, i stedet bare at skrive, at det ikke virker.
Avatar billede arne_v Ekspert
30. marts 2021 - 21:00 #26
Så vidt jeg ved er mysqli get_result + fetch unbuffered.

Kombeneret med XmlWriter  med en flush per 1000 linier bør det holde memory forbrug nede.
Avatar billede Morten Professor
30. marts 2021 - 21:09 #27
arne_v Fedt jeg prøver det af :-)
acore Helt sikkert ligger lige noget kode op hvis jeg ikke får det til at virke.
Jeg syntes det er så fedt i hjælper mig. Jeg prøver lige de ting af og vender tilbage.
Avatar billede Morten Professor
31. marts 2021 - 07:24 #28
Arne_v hold da op det virker perfekt nu.
Så vil jeg bare lige høre en ting mere.
Vil det være sådan at man kan gå rund på siden uden at opdatere. feks. hvis en gang om dagen opdatere når man går rund på siden. Jeg har noget cronjob jeg kan slå til, så den kan opdatere filen om nattet da der bliver købt en del om dagen.
Vil det være muligt at endten opdatere de enkelte linjer i xml filen, eller skal den hele databasen igennem igen. eller kan jeg kigge på id fra en varer hvor der måske er udsolgt. hvor den så kun ændre den linje i filen.

Ej hvor er det bare så fedt!!! Har kæmpet med det i nogle dage, og nu lykkedes det med hjælp. Er så glad for den hjælp jeg får.
Avatar billede acore Ekspert
31. marts 2021 - 17:43 #29
Godt gået! Tillykke. Du har kæmpet.

Du kan godt lave en sammenligning mellem resultatet af dit query og din xml. Så skal du læse filen med xml reader. Du sammenligner en record ad gangen. Hvis de ikke er ens, skal du beslutte dig for, om det er nye records eller slettede records i databasen (idet jeg går ud fra, at rettede ikke kan forekomme). Det kan du kun gøre ved at læse en linie til fra hver kilde. Så skriver du til en y xml fil.Så det kan lade sig gøre, men er ikke helt ukompliceret.

Men måske har du et timestamp, du kan bruge til at detektere "ændringer siden sidst"? Det er lettere.

Jeg synes også det er værd at du spørger dig selv hvorfor? Kan du ikke bare køre koden om natten en gang med cronjob? Skidt med, at det tage rlang tid, hvis bare det virker. Computerkraften er billig.

Det er svært for mig at gennemskue, hvorfor du ikke bare kører den, du nu har lavet. Men ellers prøv at forklare det,
Avatar billede Morten Professor
01. april 2021 - 22:34 #30
God aften :-)

Takker det er fedt det lykkedes, skal bare i gang med at kunne redigere / opdatere i filen. Når der sker en ændring i prisen
Det bliver brug i et nyhedsbrev, og nyhedernes priser kan godt svinge.
Så må lige prøve det du forslår.
Se om jeg kan få det til at spille.

1000 tak for hjælpen til jer begge, det har fået mig frem i en god retning.

Når jeg har nye spørgsmål opretter jeg et nyt, så der kan være et nyt indlæg man kan søge på her inde.

Hav det rigtig godt

Med venlig hilsen
Morten
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

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