Avatar billede chrisrj Forsker
31. oktober 2018 - 11:59 Der er 50 kommentarer og
2 løsninger

Kombinere mange id'er til en kort string.

Hejsa

Jeg har en lang række id'er ("normalt" mellem 1000 og 1 million stk.) jeg skal have kortet ned til en lille streng ala [1-1000] eller [1-243, 355-9876].
Altså kan jeg ikke regne med at de er i rækkefølge.

Så hvordan gør jeg det nemmest og hurtigst?
Avatar billede ejvindh Ekspert
31. oktober 2018 - 12:08 #1
Prøv at sige lidt om, hvordan du "har" dem:
Ligger de i et array eller i enkeltstående variable?
Er der nogen logik i, hvordan de er gemt?
Er det vigtigt at den resulterende streng har en bestemt rækkefølge?
Avatar billede chrisrj Forsker
31. oktober 2018 - 12:15 #2
De kommer fra en database, så array er vel mest nærliggende. :)

De bliver gemt i grupper af forskellige størrelser - derfor de 1000 <-> 1 million.

Rækkefølgen er vigtig på den måde, at det skal være muligt at "rode" den ud igen til visning på skærmen. Her i kombination med andre række i tabellen.

Helt overordnet er dette en del af en form for breadcrumb spor, som ikke skal fylde for meget, men stadig skal kunne vises til brugeren. Altså at man kan følge eet id hele vejen.
Avatar billede chrisrj Forsker
31. oktober 2018 - 12:18 #3
Ah, og jeg henter dem i nummerorden (ORDER BY), naturligvis! ;)
Avatar billede ejvindh Ekspert
31. oktober 2018 - 12:24 #4
Nu spurgte du jo til det nemmeste og hurtigste, og her vil jeg mene, at json_encode må være svaret, da den jo klemmer et array ned til en streng -- der så igen kan gøres til et array vha. json_decode.

Men det kan godt være, den ikke helt lever op til dit tilføjede ønske om også at skulle "fylde for meget".
Avatar billede chrisrj Forsker
31. oktober 2018 - 12:27 #5
"fylde for meget" skal ses i forhold til at skrive hvert enkelt id i strengen... :)
Avatar billede acore Ekspert
31. oktober 2018 - 12:42 #6
Avatar billede chrisrj Forsker
31. oktober 2018 - 12:56 #7
Hmm... bruger jeg dette, så fejler serveren med en 503'er.

Jeg ved godt, at det 10 x hvad jeg bad om, men alligevel...er det koden, serveren eller php der ikke kan klare det?


for($j=0; $j<=1999999; $j++) {
  $arr[] = $j;
}
function newItem($start, $prev)
{
    if ($start == $prev)
    {
        $result = $start;
    }
    else
    {
        $result = $start . '-' . $prev;
    }

    return $result;
}

foreach($arr as $item)
{
    if ($prev)
    {
        if ($item != $prev + 1)
        {
            $newarr[] = newItem($start, $prev);
            $start = $item;
        }
    }
    else
    {
        $start = $item;
    }
    $prev = $item;
}

$newarr[] = newItem($start, $prev);

echo implode(',', $newarr);
Avatar billede acore Ekspert
31. oktober 2018 - 13:03 #8
Afprøv det med et mindre tal end 1999999. Hvis det virker med 1000, men ikke med det tal, så er det næppe koden.

Bortset fra det, så bør koden ikke køre uden fejl under PHP 7, da $newarr ikke er initialiseret som array.
Avatar billede chrisrj Forsker
31. oktober 2018 - 13:05 #9
Jeg har prøvet med mindre, og der virker det fint. :)

Serveren kører 5.6.x
Avatar billede acore Ekspert
31. oktober 2018 - 13:12 #10
Kan det være max eksekveringstid, der er for lang? Prøv at tilføje som linie 1

ini_set("max_execution_time", 600); // in seconds
Avatar billede chrisrj Forsker
31. oktober 2018 - 13:16 #11
Jeg har lige fundet at det er memory grænsen. :D

1848104 er max.
Avatar billede acore Ekspert
31. oktober 2018 - 13:18 #12
og du kan ikke ændre den med

ini_set("memory_limit", "2048M"); // Mb
Avatar billede chrisrj Forsker
31. oktober 2018 - 13:23 #13
Det kunne jeg sørme! :)
Avatar billede acore Ekspert
31. oktober 2018 - 13:28 #14
Lucky you ;)
Avatar billede chrisrj Forsker
31. oktober 2018 - 13:29 #15
Så mangler jeg "bare" at vide mig sikker på, at det er nemt at finde "stien" i disse strenge efterfølgende...ja, det burde jeg nok have startet med! :D

Altså, når jeg har rækker som disse:
1-5,7,9-12,15-16
1-3,9-12
2-5,15-16
1-7,9-12

Og derefter vil se de rækker hvor f.eks. 2 eller 10 er med.
Avatar billede acore Ekspert
31. oktober 2018 - 13:43 #16
Er det absolut i tekststrengen du vil kunne se efter, eller kan det gemmes lidt smartere?
Avatar billede chrisrj Forsker
31. oktober 2018 - 13:45 #17
Jeg er åben for forslag. :)
Avatar billede acore Ekspert
31. oktober 2018 - 14:43 #18
Noget i stil med

$haystack =
  array(
    array("min" => 1, "max" => 5),
    array("min" => 7, "max" => 7),
    array("min" => 9, "max" => 12),
    array("min" => 15, "max" => 16)
  );

$needle = $_GET["n"];

echo("Is ".$needle." in [".HaystackToString($haystack)."]? ".(isNeedleInHaystack($needle, $haystack)? "Yes": "No"));

function isNeedleInHaystack($needle, $haystack)
{
  foreach($haystack as $item)
  {
    if ($needle < $item["min"]) break;
    if ($needle >= $item["min"] && $needle <= $item["max"]) return true;
  }
  return false;
}

function HaystackToString($haystack)
{
  $s = "";
  foreach($haystack as $item)
  {
    if ($s != "") $s .= ",";
    if ($item["min"] == $item["max"])
      $s .= $item["min"];
    else
      $s .= $item["min"]."-".$item["max"];
  }
  return $s;
}
Avatar billede chrisrj Forsker
31. oktober 2018 - 14:49 #19
Hmm...

Mjah, ikke helt.

Der vil være mange række - tusindvis som skal søges i gennem, og hver række hvor id'et findes er en del af resultatet.

Så f.eks. id 2 findes i rækkerne 1, 4, 14, 1007 osv.
Avatar billede chrisrj Forsker
31. oktober 2018 - 15:45 #20
Ah, og så er der jo lige en detalje, at rækkerne jo ligger i databasen, så det er jo egentligt SQL jeg skal bruge her...
Avatar billede acore Ekspert
31. oktober 2018 - 15:58 #21
#18 kan udvides med et snuptag til at give det, du beder om i #19.

Men måske du skulle specificere hvor og hvordan du har data og hvad du ønsker først, og så kan vi svare på det.

Ellers bliver det lidt for meget gætteri, synes jeg.
Avatar billede chrisrj Forsker
31. oktober 2018 - 16:06 #22
Jamen, data er jo dem vi laver i starten af spørgsmålet - de skal jo smides ind i en database, for så at kunne hive det ud igen på den nævnte måde. :)

Prekonditioner:
1: id'er er oprettet i databasen i anden tabel - er gjort
2: Nogle id'er skal skifte ejer - dette ejerskifte skal registres for hvert id - er gjort med løsningen i #7, taget fra dit link i #6. :)

Derefter:
3: Søg efter eet id i alle rækker og vis dem
Avatar billede chrisrj Forsker
31. oktober 2018 - 16:19 #23
Det her vist det tætteste jeg kan google mig til. Men stadig ikke helt nok. :-/

https://stackoverflow.com/questions/25134413/mysql-query-checking-if-integer-between-range-in-single-column-string
Avatar billede chrisrj Forsker
31. oktober 2018 - 16:22 #24
Ej, ved du hvad. Jeg laver sgu et nyt spørgsmål i et db kategori. Det er nok bedre. :)

Jeg takker for hjælpen, og du skal da være velkommen i det nye spørgsmål. :)
Avatar billede acore Ekspert
31. oktober 2018 - 16:23 #25
Dine data er

1-5,7,9-12,15-16
1-3,9-12
2-5,15-16
1-7,9-12

repræsenteret i din database som

no    range minid maxid
1    1    1    5
2    1    7    7
3    1    9    12
4    1    15    16
5    2    1    3
6    2    9    12
7    3    2    5
8    3    15    16
9    4    1    7
10    4    9    12

SELECT range FROM haystacks WHERE needle >= minid AND needle <= maxid

hvor du erstatter needle med den værdi, du vil søge efter
Avatar billede chrisrj Forsker
31. oktober 2018 - 16:29 #26
Hmm....interessant løsning!

Men det giver dog mange flere rækker i databasen...ser du ingen bedre måde? :)
Avatar billede acore Ekspert
31. oktober 2018 - 16:31 #27
Mange rækker i en database er vel intet problem. Hvordan vil du ellers komme det i databasen? jeg synes dette er at foretrække i forhold til en masse tekststrenge, som du ikke kan søge i.
Avatar billede chrisrj Forsker
31. oktober 2018 - 16:33 #28
Mange rækker * mange rækker er RIGTIGT mange rækker. :D

Lol, jamen hvis jeg vidste det, spurgte jeg jo ikke her! :D

Ja, det er jo det interessante ved løsningen. ;)
Avatar billede acore Ekspert
31. oktober 2018 - 16:46 #29
Fordelen med #25 er, at du kan finde alle nåle i høstakken med én SELECT.

Alternativet er, at hive alle data ud af databasen 8som så har færre rækker) en række ad gangen og køre PHP for at finde ud af om der er et match. Jeg gætter på, at det er meget mindre effektivt.

Men det er ret let at teste:

1. Lav to tomme tabeller i databasen - en med tekst-strenge og en som i #25. husk index på minid og maxid
2. Lav x tusind tilfældige tal i stigende rækkefølge om fyld dem i begge tabeller
3. Kør en test og tag tid
4. Fortæl her om resultatet
Avatar billede chrisrj Forsker
31. oktober 2018 - 16:49 #30
Jeg er SLET ikke i tvivl om at hive rækkerne ud og tjekke dem igennem er en dårlig løsning. ;)

Jeg tænker bare, om man kunne lave en funktion i MySQL eller nowwet...måske det ville være hurtigere?
Avatar billede acore Ekspert
31. oktober 2018 - 17:03 #31
Det vil jeg ikke afvise. Det vigtigste er nok at have index på minid og maxid, samt RAM nok på SQL serveren.
Avatar billede chrisrj Forsker
31. oktober 2018 - 17:06 #32
Det er et webhotel, så ressourcerne er jo begrænset...

Anyway, jeg kigger på det imorgen. :)

Og siger tak for hjælpen i dag, det var ikk' så ring' endda. :)
Avatar billede chrisrj Forsker
01. november 2018 - 14:53 #33
Nå, nu nåede jeg endelig igennem de fixbare bugs, og stødte så på ny: Unsupported operand types

I denne linie:
if ($item != $prev + 1)


Google antyder at det er fordi en af dem er et objekt eller lign.

Hele funktionen:
function CombineSeedIDs($SeedIDs = array()) {
   
    if (!is_array($SeedIDs)) {
        return null;
    }
   
    foreach($SeedIDs as $item) {
        if ($prev) {
            if ($item != ($prev + 1)) {
                $newarr[] = array($start, $prev);
                $start = $item;
            }
        } else {
            $start = $item;
        }
        $prev = $item;
    }

    $newarr[] = array($start, $prev);
   
    return $newarr;
}

og dens data kommer herfra:
$result = mysqli_query($mysqli, 'SELECT SeedID FROM '.UBIC_ProtoUBISeeds.' WHERE CurrentOwnerAID='.$FromAID.' ORDER BY SeedID ASC LIMIT '.$Amount);
if ($result) {
while ($row = $result->fetch_assoc()) {
                $array[] = $row;
            }
            $SeedIDs[] = CombineSeedIDs($array);
...
}
Avatar billede chrisrj Forsker
01. november 2018 - 15:42 #34
Nå, så kom jeg da fordi dén fejl! :D

if ($item["SeedID"] != ($prev["SeedID"] + 1)) {

Til gengæld for jeg nu et mærkeligt array ud af det:
array(1) { [0]=> array(1) { [0]=> array(2) { [0]=> array(1) { ["SeedID"]=> string(7) "1000001" } [1]=> array(1) { ["SeedID"]=> string(7) "1005000" } } } }

Det burde jo bare være 1000001 og 1005000.

Så hvad pokker går det galt??
Avatar billede acore Ekspert
01. november 2018 - 16:05 #35
$item er et array.

Jeg vil mene, at hver gang du bruger en værdi fra $item, skal der stå ($item["SeedID"]. Til gengæld er ideen, at $prev og $start skal være værdier, så sammenligningen skal være:

if ($item["SeedID"] != ($prev + 1)) {

Endelig er der et problem (der muligvis ikke betyder noget, men er dårlig kode) med initialisering af $newarr og $prev. Det vil i hvert fald ikke køre i PHP 7.
Avatar billede chrisrj Forsker
01. november 2018 - 16:10 #36
Ja, jeg bruger jo oldtidsphp, så her brokker den sig kun over $prev ;)

Dette fixer det hos mig: $prev = null;

At fjerne ["SeedID"] fra $prev gør $newarr helt tom! :O

    foreach($SeedIDs as $item) {
        if ($prev) {
            if ($item["SeedID"] != ($prev + 1)) {
                $newarr[] = array($start, $prev);
                $start = $item;
            }
        } else {
            $start = $item;
        }
        $prev = $item;
    }
Avatar billede acore Ekspert
01. november 2018 - 16:20 #37
Alle steder, hvor der står $item, skal der stå $item["Seed"]

Du bør inkludere

$newarr = array();

som initialisering sammen med

$prev = null;
Avatar billede acore Ekspert
01. november 2018 - 16:21 #38
...undtagen selvfølgelig i foreach sætningen

ps: har ikke testet
Avatar billede chrisrj Forsker
01. november 2018 - 16:28 #39
Ah, naturligvis. :) (og så er det SeedID ;p )

Det gav et lidt bedre resultat:
array(1) { [0]=> array(1) { [0]=> array(2) { [0]=> string(7) "1000001" [1]=> string(7) "1005000" } } }

Men ikke helt som det bør være. :-/
Avatar billede acore Ekspert
01. november 2018 - 16:30 #40
post lige hele koden igen, som den ser ud nu
Avatar billede chrisrj Forsker
01. november 2018 - 16:33 #41
Hehe, godt så. :p


        $result = mysqli_query($mysqli, 'SELECT SeedID FROM '.UBIC_ProtoUBISeeds.' WHERE CurrentOwnerAID='.$FromAID.' ORDER BY SeedID ASC LIMIT '.$Amount);
if ($result) {
            $array = [];
            while ($row = $result->fetch_assoc()) {
                $array[] = $row;
            }
$SeedIDs[] = CombineSeedIDs($array);
            var_dump($SeedIDs);
            // insert the trail rows
            foreach($SeedIDs as $item) {
                if (is_array($item)) {
                    $result = mysqli_query($mysqli, 'INSERT INTO '.UBIC_SeedTrail.' (STID, MinSeedID, MaxSeedID, TID) VALUES (DEFAULT, '.$item[0].', '.$item[1].', '.$TID.')');
                } else {
                    $result = mysqli_query($mysqli, 'INSERT INTO '.UBIC_SeedTrail.' (STID, MinSeedID, MaxSeedID, TID) VALUES (DEFAULT, '.$item.', '.$item.', '.$TID.')');
                }
               
            }
...
}

function CombineSeedIDs($SeedIDs = array()) {
   
    if (!is_array($SeedIDs)) {
        return null;
    }
   
    $prev = null;
    $newarr = array();
   
    foreach($SeedIDs as $item) {
        if ($prev) {
            if ($item["SeedID"] != ($prev + 1)) {
                $newarr[] = array($start, $prev);
                $start = $item;
            }
        } else {
            $start = $item["SeedID"];
        }
        $prev = $item["SeedID"];
    }

    $newarr[] = array($start, $prev);

    //echo implode(',', $newarr);
   
    return $newarr;
}
Avatar billede acore Ekspert
01. november 2018 - 16:38 #42
$start = $item;

skal være

$start = $item["SeedID"];
Avatar billede chrisrj Forsker
01. november 2018 - 16:40 #43
Ah, godt fanget! ;)

Men det ændrer ikke noget. :-/
Avatar billede acore Ekspert
01. november 2018 - 16:45 #44
og

$SeedIDs[] = CombineSeedIDs($array);

skal være

$SeedIDs = CombineSeedIDs($array);

og den skal dumpe

array(1) { [0]=> array(2) { [0]=> string(7) "1000001" [1]=> string(7) "1005000" } }
Avatar billede chrisrj Forsker
01. november 2018 - 16:49 #45
Uhh, du go'! :D
Avatar billede acore Ekspert
01. november 2018 - 16:50 #46
Øvelse gør mester ;)
Avatar billede chrisrj Forsker
01. november 2018 - 16:52 #47
Jaw jaw. ;)

Så er der bare den sidste! :D

            $result = mysqli_query($mysqli, 'UPDATE '.UBIC_ProtoUBISeeds.' SET CurrentOwnerAID = '.$ToAID.' WHERE SeedID IN ('.implode(',',$array).'))');

Den fejler med en Array to string conversion

Det er arrayet fra while løkken.
Avatar billede chrisrj Forsker
01. november 2018 - 19:37 #48
Harh, ku sel! :p (med lidt gooling, men altså.. ;p )

implode(',', array_map(function ($entry) {
                return $entry['SeedID'];
                }, $array));
Avatar billede chrisrj Forsker
01. november 2018 - 19:45 #49
Eller dvs...nu er der i hvert fald ikke php fejl. :D

Sql derimod ser ikke ud til at virke korrekt. :-/

Jeg havde ellers den opfattelse, at man kunne opdatere mange rækker vha en WHERE id IN (id, id, id,...). Men det kan man åbenbart ikke? :-/
Avatar billede chrisrj Forsker
01. november 2018 - 19:49 #50
Ah, bare et parentes problem.

NU spiller det sgu!! :D
Avatar billede acore Ekspert
01. november 2018 - 20:40 #51
Super 😀
Avatar billede chrisrj Forsker
01. november 2018 - 21:41 #52
Jaw jaw. :)

Så tror jeg, at jeg siger tak for kampen - og tålmodigheden! :) :D

Det var ikke så lidt. :) :)
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