Autor Zpráva
Matyáš
Profil
Dobrý den,
Řeším problém, jak nejjednodušeji uložit do pole hodnoty pouze z 1 sloupce SQL databáze.
Napadá mě pouze toto "dřevorubecké" řešení:
$tabulka = mysql_query("SELECT sloupec FROM tabulka");
    while($radek = mysql_fetch_array($tabulka))
    {
      $radek[0] už je ona hodnota...
    }
Ale to mi připadá opravdu stupidní.
Nevím, proč nefungovalo pouze:
$tabulka = mysql_query("SELECT sloupec FROM tabulka");
$tabulka = mysql_fetch_array($tabulka);
foreach($tabulka as $neco)
{
   echo $neco.",";
}
Jenže toto vyhodí pouze 1. hodnotu ve sloupci.
Nenapadá někoho nějaké jednodušší řešení, než to výše uvedené?
Děkuji za odpověď!
lionel messi
Profil
Matyáš:
Nevím, proč nefungovalo pouze:

Pretože mysql_fetch_array pri jednom zavolaní vráti vždy len jeden riadok.

Mimochodom, lepšie je použiť mysql_fetch_assoc a najlepšie je nepoužívať zastaralú extenizu mysql_* vôbec, ale prejsť na MySQLi (prípadne PDO/dibi).
juriad
Profil
Matyáš:
Přečti si, co dělá funkce mysql_fetch_array.
Vrátí jeden řádek výsledku jako pole a posune interní ukazatel na další, nebo vrátí FALSE, pokud už řádky došly.

Jelikož každé neprázdné pole je vyhodnoceno jako true, smyčka while probíhá, dokud existují řádky. V tom druhém případě si pomocí funkce mysql_fetch_array řekneš o jeden řádek a dostaneš s největší pravděpodobností pole (nemusel bys, pokud by tabulka byla prázdná). A pak tímto polem iteruješ (jelikož jsi si řekl v dotazu o jeden sloupec, proběhnou dvě iterace - číslelný index a názvový index).

Místo rozšíření mysql_ použij něco modernějšího, napřiklad dibi, viz dibiphp.com/cs/quick-start#toc-ziskavani-vysledku.
Matyáš
Profil
Děkuji za Vaše názory. Budu tedy používat novější msqli.
Našel jsem metodu msqli_fetch_all, která téměř splňuje to, co chci;
$ids = mysqli_query($connection, "SELECT id FROM inzeraty");
    $tabulka = mysqli_fetch_all($ids, MYSQLI_NUM);
    foreach($tabulka as $radek)
    {
      echo $radek[0]."<br>";
    }
Jenže k jednotlivému řádku musím přistupovat přes index 0, jiné tam stejně nejsou.
To je mi ale k ničemu.
Já mám totiž v sloupci v databázi pouze čísla a chci z nich vzít minimum, maximum a porovnat ho (array_diff) s jiným polem, což ovšem s tímto výsledkem také nejde.
Děkuji za Vaše názory.
Dan Charousek
Profil
Nestačí si výsledky uložit do pomocného pole?

    // ...připojení
    // ... select
    // ... fetch
    $temp = [];
    foreach($tabulka as $radek) {
        $temp[] = $radek[0];
    }
tiso
Profil
Matyáš: „Já mám totiž v sloupci v databázi pouze čísla a chci z nich vzít minimum, maximum
To sa dá vytiahnuť naraz:
SELECT MIN(sloupec) minimum, MAX(sloupec) maximum FROM tabulka
Dan Charousek
Profil
tiso:
To je taky možnost, ale nezahrneš do toho potom: „a porovnat ho (array_diff) s jiným polem
tiso
Profil
Dan Charousek: aha, tak to bolo takto myslené
Matyáš: koľko máš hodnôt v tom stĺpci?
Matyáš
Profil
tiso:
Jsou to ID inzerátů. Maximum je asi 14000. Ale nerad bych to nějak omezoval.
Celé to funguje takto:
ID je primární klíč databáze. Slouží tak k identifikaci jednotlivých inzerátů.
Po odebrání inzerátu vznikne volné id. Nechci používat AUTO_INCREMENT, protože to slepě přidává hodnoty, a já chci, aby se znovu použilo nejnižší volné ID.

To znamená:
1. Vybrat všechny id v databázi.
2. Vzít maximální hodnotu id z ↑
3. Vyplnit nějaké pole hodnotami od 1 do maximální hodnoty id ↑
4. Porovnat toto pole s tím, které jsem získal v bodě 1 → vznikne pole s volnými ID.
5. Vybrat nejnižší hodnotu z volných ID ↑ a tu přiřadit novému inzerátu.

V PHP jsem si vytvořil tuto funkci:
function get_id($ids)
{
  $max = max($ids);
  $range = range(0,$max);
  $chybejici = array_diff($ids, $range);
  return min($chybejici);
}
která má vstupní parametr pole s ID z databáze a vrací nejnižší volné ID.
Jenže je tu ten problém, jak dostat do pole hodnoty z jednoho sloupce databáze.
Abych byl názornější, tady je příklad:
Obsah databáze:
|ID|název atd...
|1|...
|3|...
|4|...
|6|...

Já potřebuji získat takové pole, aby:
v $pole[0] byla 1
v $pole[1] byla 3
v $pole[2] byla 4
...
A pak by funkce get_id měla vrátit hodnotu 2.
juriad
Profil
Matyáš:
Nedělej to. Řekni nám své důvody a my ti je všechny vyvrátíme.
Matyáš
Profil
juriad:
:D
Děkuji za odkaz na zajímavý článek.
Já však nechci po smazání inzerátu VŠECHNY přesunout, aby tam nebyla mezera, ale chci ty mezery vyplňovat. Až se vyplní, můžeme vytvářet nové ID.
Jde mi o to (to mi snad nemusí nikdo vysvětlovat), že:
1. Větší čísla zabírají zbytečně víc místa.
2. Je klidně možné, že těch inzerátů bude jen 100 a já pak nechci mít id těch inzerátů třeba 17346564218+. (to je stupidní)
3. Detaily o inzerátech zobrazuji na této stránce: něco/inzeraty.php?id=číslo, takže nechci, aby v tom odkaze byly zbytečně velká čísla (aby se to případně i dalo zapamatovat).

Vím o řešení, že bych si vytvořil další sloupec s (user-friendly ID) ale to já nechci - to odporuje bodu 1.
Nechci však působit tak, že si nenechám poradit - jen mě zatím nenapadá ani jedno + u hloupé inkrementace id.
Vždyť problém ve funkci by z principu neměl nikdy nastat...
mimochodec
Profil
Matyáš:
Jak by se ti líbilo, kdyby tě zaujal inzerát inzeraty.php?id=111, stránku zapamatoval (jak píšeš) nebo přidal do oblíbených, za týden se tam vrátil a místo čtyřkolky tam byl pudl?

Tvůj návrh je opravdu špatný. Jestli chceš krátkou adresu, použij třeba alfanumerický kód. Už při čtyřech znacích dostaneš solidní rozsah (26+26+10)^4.
juriad
Profil
1. Nezabírají, každé číslo typu INT (rozsah -2147483648 až 2147483647) zabírá přesně to samé místo, 4 byty. A 2 miliardy inzerátů nebudeš mít ani omylem.
2. Neboj, pokud se dostaneš do pěticiferných čísel, tak budeš mít hodně velké štěstí. Počítej také s tím, že inzerát má nějakou životnost, takže není možné mít hodně neplatných inzerátů a málo platných (při měsíční životnosti se poměr dostane nad 100 až po 8 letech).
3. Nikdo adresu nezadává ručně. A nikdo si to čislo nebude muset pamatoval, leda bys jej diktoval někomu po telefonu. Naopak, lidé, co si uloží inzerát do záložek se k němu později vrátí a uvidí úplně jiný - nedozví se, že ten, který je zajímal už není platný.
4. Větší čísla vypadají důvěryhodněji - je vidět, že server funguje a lidé jej používají.

Ten mimochodcův alfanumerický kód je dobrý nápad, ale stejně v databázi nechej ID číslené a jen jej konveruj do podoby pro URL (odkázaná funkce umí číslice + velká písmena, což při 4 znacích znamená čísla až do 1679616).
Joker
Profil
Matyáš [#11]:
ID je primární klíč databáze. Slouží tak k identifikaci jednotlivých inzerátů.
Po odebrání inzerátu vznikne volné id. Nechci používat AUTO_INCREMENT, protože to slepě přidává hodnoty, a já chci, aby se znovu použilo nejnižší volné ID.

Ty dva řádky se vzájemně vylučují.
Buď může ID být primární klíč, v tom případě musí jednoznačně identifikovat záznam, takže ani nesmí být nikdy přiděleno jinému záznamu.
Nebo se ID může přidělovat různým záznamům, v tom případě neidentifikuje jednoznačně konkrétní záznam a nemělo by být primárním klíčem.

Jde mi o to (to mi snad nemusí nikdo vysvětlovat), že
Zjevně musí, předpokládáte věci, které nejsou pravda.

1. Větší čísla zabírají zbytečně víc místa.
Ne, toto není pravda.
Obsazené místo není dané hodnotou, ale datovým typem.
Navíc číselné typy zabírají tak málo místa, že zabývat se tímhle je v 99 % případů nesmyslné.

2. Je klidně možné, že těch inzerátů bude jen 100 a já pak nechci mít id těch inzerátů třeba 17346564218+. (to je stupidní)
Tak jednak i při otáčení 100 inzerátů každý den by dostat se na ta takové číslo trvalo přes 475 248 let. S nejvyšší pravděpodobností se nedostanete na víc než 4-5 číslic.
Navíc ID inzerátu prakticky nikoho nezajímá.

3. Detaily o inzerátech zobrazuji na této stránce: něco/inzeraty.php?id=číslo, takže nechci, aby v tom odkaze byly zbytečně velká čísla (aby se to případně i dalo zapamatovat).
Viz výše: Pokud hodláte stejné ID používat pro různé záznamy, není to jednoznačný identifikátor záznamu.
V tom případě by se neměl používat ani v odkazech.
Jelikož takové ID nenese ani informaci o tom záznamu, je vlastně úplně zbytečné (i když teoreticky by jako primární klíč mohla sloužit kombinace ID a data vytvoření inzerátu).
Matyáš
Profil
mimochodec:
Jak by se ti líbilo, kdyby tě zaujal inzerát inzeraty.php?id=111, stránku zapamatoval (jak píšeš) nebo přidal do oblíbených, za týden se tam vrátil a místo čtyřkolky tam byl pudl?

juriad:
3. Nikdo adresu nezadává ručně. A nikdo si to čislo nebude muset pamatoval, leda bys jej diktoval někomu po telefonu. Naopak, lidé, co si uloží inzerát do záložek se k němu později vrátí a uvidí úplně jiný - nedozví se, že ten, který je zajímal už není platný.

Ano. To je pravda. Na to jsem pomyslel, ale nevěnoval jsem tomu velkou pozornost, nemyslel jsem si, že by z toho mohl být velký problém. Člověk ví, na co si uložil odkaz - takže kdyby se tam objevilo něco jiného, každý by pochopil, že to není to, co hledá.
Ale je pravda, že to je pro uživatele velice nepříjemné.

juriad:
1. Nezabírají, každé číslo typu INT (rozsah -2147483648 až 2147483647) zabírá přesně to samé místo, 4 byty. A 2 miliardy inzerátů nebudeš mít ani omylem.

Joker:
Tak jednak i při otáčení 100 inzerátů každý den by dostat se na ta takové číslo trvalo přes 475 248 let. S nejvyšší pravděpodobností se nedostanete na víc než 4-5 číslic.

To číslo 17346564218 byla samozřejmě hyperbola...

Matyáš:
1. Větší čísla zabírají zbytečně víc místa.

To beru zpět. Nevěděl jsem, že to tak funguje i v databázi. Nevšiml jsem si, že se tam dá nastavit INT i BIGINT, ale jen INT a pak tedy to, kolik zabere místa by záleželo na velikosti čísla.

Ten mimochodcův alfanumerický kód je dobrý nápad, ale stejně v databázi nechej ID číslené a jen jej konveruj do podoby pro URL (odkázaná funkce umí číslice + velká písmena, což při 4 znacích znamená čísla až do 1679616).

To je dobrý nápad. Udělám to tak.

Joker:
Ty dva řádky se vzájemně vylučují.
Buď může ID být primární klíč, v tom případě musí jednoznačně identifikovat záznam, takže ani nesmí být nikdy přiděleno jinému záznamu.
Nebo se ID může přidělovat různým záznamům, v tom případě neidentifikuje jednoznačně konkrétní záznam a nemělo by být primárním klíčem.

Tady jsem zřejmě nebyl správně pochopen; v databázi by v určitém čase byl pouze 1 inzerát s daným ID, nikoli více. To by obstarávala funkce get_id (i když tam chyběla výjimka pro případ když v databázi nebude žádný inzerát, nebo když tam nebude mezera atd...)

Děkuji všem za Vaše rady, udělám to tak, jak jste mi poradili.
Joker
Profil
Matyáš:
To číslo 17346564218 byla samozřejmě hyperbola...
Jasně, ale podstatou mého sdělení bylo, že ten problém zveličujete, protože nad 4-5 číslic se stejně nedostanete.

Tady jsem zřejmě nebyl správně pochopen; v databázi by v určitém čase byl pouze 1 inzerát s daným ID, nikoli více.

Já to pochopil, ale smazání záznamu v tabulce nezpůsobí, že ten inzerát, jak by napsal Orwell, „neexistuje a nikdy neexistoval“.
Měl bych inzerát, smazal ho, jeho ID přidělil jinému a někdo mi poslal dotaz na to ID. V tu chvíli na základě toho ID nelze jednoznačně říct, jestli se dotaz týká toho aktuálního inzerátu, nebo toho smazaného.
Proto jsem psal, že ID pak není jednoznačný identifikátor.

A pro úplnost dodám, že tam je i riziko zneužití:
Založím nějaký legrační inzerát, rozešlu odkazy. Počkám, až se odkaz rozšíří, inzerát smažu a hned založím nový inzerát s úplně jiným obsahem. Ten dostane identifikátor a tím i adresu toho předchozího, takže ty odkazy teď povedou na ten jiný inzerát.
Matyáš
Profil
Ještě bych se chtěl zeptat, zda je možné předem zjistit, jaké id dostane nový inzerát (když má sloupec auto-increment)
Ke každému inzerátu budu totiž nahrávat fotky a chci je pojmenovat takto id_cislofotky.xxx, takže k tomu potřebuji znát id.
Šlo by nejdřív uložit údaje do databáze a pak zjistit ID, které inzerát obdržel, ale to nevím, jak bych udělal - když nevím id.
Popř. mi navrhněte nějaký lepší způsob, jak pojmenovat fotky patřící k inzerátům :)
Děkuji
Dan Charousek
Profil
Matyáš:
K tomu slouží funkce mysqli_insert_id()
juriad
Profil
Matyáš:
To opět nedělej. Mohlo by se stát, že dva uživatelé budou vytvářet inzeráty najednou a všechny fotky by pak dostal jeden z těch inzerátů.
Jelikož víme, že ID inzerátu nejsou problém (i když na každý inzerát připadne 10 takových, které se nedokončí), můžeš vytvořit prázdný inzerát a ten pak editovat. Inzerát by tak měl navíc jeden další přiznak - totiž zda je publikovaný (pokud není, veřejně se nezobrazuje, jen jej jeho majitel může editovat). Fotky tedy uživatel přidává vždy k existujícímu inzerátu - má tedy známé ID. Funkci na zjištění ID nového inzerátu ti poradil Dan Charousek.

Tabulka fotky bude mít alespoň čyři sloupce:
(id int primary key auto_increment, inzerat int not null, filename varchar(200) not null, added datetime not null)

Fotky můžeš pojmenovávat jak chceš, jen počítej s tím, že je dobré je rozhazovat do adresářů, protože jednak FTP klienti nezvládají velké tisíce souborů v adresáři a také je vyhledání fotky ve velkém adresáři malinko pomalejší. Často se jako název fotky volí přímo její ID nebo její hash (sha1). Pak se rozhodí do adresáře třeba podle (ID modulo 100) nebo podle prvních dvou znaků hashe. Budeš mít třeba 100 nebo 256 adresářů do kterých se fotky budou postupně rozhazovat. To v kterém skutečně jsou poznáš z databáze podle filename nebo id. Proti pojmenování podle ID stojí automatické stahování fotek; škůdce/uživatel/kdokoli by mohl stáhnout všechny, protože ví, že ID jsou popořadě. V případě hashů, to nehrozí, ale za cenu delšího názvu (v připadě sha1 je to název o délce 40 znaků, pokud jej nebudeš zkracovat).

Možná ti přijde blbé, že ty jako správce nepoznáš z názvu a umístění, kterému inzerátu fotka patří, ale uvědom si, že jakmile jich budou statisíce, už by ti ta informace byla nanic. Navíc to vždy můžeš zjistit v databázi.
Matyáš
Profil
juriad a Dan Charousek:
Děkuji za užitečnou odpověď.
Já nejdřív provádím příkaz INSERT INTO a do databáze uložím všechny data a do sloupečku fotky uložím 0...
A po něm beru mysqli_insert_id. To znamená, že v databázi už je záznam. Tím je vyřešen problém s dvěma uživateli ve stejnou dobu.
Potom se provádí upload fotek. Pokud se to podaří (dohromady jejich velikost odpovídá limitu atd...), vložím do databáze do sloupce fotky počet uploadovaných fotek.
Potom když inzerát zobrazuji, tak pokud má ve sloupci fotky 0, zobrazím jen fotku default.xxx a pokud tam má číslo > 0 tak zobrazim všechny fotky s názvem (třeba) id_1.xxx až id_n.xxx
Je to hlavně kvůli ušetření místa v databázi...
Je to OK? To pojmenovávání fotek ještě promyslím.
Děkuji.
juriad
Profil
Matyáš:
Nemá smysl takto šetřit a počítat si v té tabulce počet fotek. Záznam v databázi bude tisíckrát menší než je velikost fotky, databáze zvládají i řádově větší objemy dat. Snažíš se o předčasnou optimalizaci v době, kdy si neuvědomuješ důsledky.
Co když uživatel nějakou fotku smaže a nahraje později jinou? To zase budeš hledat díry a aktualizovat počty fotek?
Vazbu na inzerát měj ze strany tabulky fotky (budeš mít doufám tabulku fotek) a nikoli jako nějaké číslo u inzerátů, ten sloupec fotky nemá správně vůbec existovat.
Jak jsme navrhoval, úplně bych zrušil jakoukoli asociaci názvu fotky s id inzerátu, o to se postará tabulka fotek.
Matyáš
Profil
Teď jsem narazil na další problém.
Mám formulář pro vyhledávání mezi inzeráty, a chci, aby se data předávala přes URL - tedy metodou GET.
Formulář odesílám tlačítkem, které má nějaké value, kterým na něm nastavuji text.
Problém je v tom, že po odeslání formuláře se do URL napíše: něco&hledat=Hledat. A to mi připadá zbytečné. Vždyť to tam bude vždycky, nejde to vyřešit jinak? (aby tam nebylo &hledat=Hledat)
Děkuji!
Dan Charousek
Profil
Tlačítku value a name atributy seber a odeslání formuláře kontroluj podle přítomnosti jiné hodnoty.
Matyáš
Profil
Nojo, ale když tlačítku seberu value, jak pak nastavím text na něm?
Dan Charousek
Profil
Matyáš:
Moje chyba,
jestli se nepletu mělo by stačit odebrat atribut name.
Matyáš
Profil
Aha, děkuji. Funguje to.
Matyáš
Profil
Teď mám další problém, se kterým si nevím rady :)
Mám nějaký select a druhý select.
Potřeboval bych, když v selectu1 vyberu určitou položku, tak aby se v selectu2 "zneaktivnily" určité možnosti a zůstala jen 1.
Rád bych to udělal přes JS, kdyby to šlo. AJAXEM bych to radši nekomplikoval... :)
Děkuji za Vaše nápady!
Dan Charousek
Profil
Matyáš:
Rád bych to udělal přes JS, kdyby to šlo. AJAXEM bych to radši nekomplikoval... :)
To se nevylučuje vzhledem k tomu, že AJAX je metoda, pomocí které se na server asynchronně posílá požadavek právě pomocí JavaScriptu.

Samozřejmě to lze udělat staticky tak, že si do proměnné v js z php vygeneruješ nějaké pole, které bude obsahovat závislosti, ale není to úplně vončo:

Ukázka

<?php

$zavislosti = [
    "vsechna" => [1,2,3,4,5,6,7,8],
    "sude" => [2,4,6,8],
    "liche" => [1,3,5,7],
    "prvocisla" => [2,3,5,7]
];

?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script>
            data = <?=json_encode($zavislosti)?>;

            function filter(value, id, data) {

                var select = document.getElementById(id);
                    select.disabled = 'disable';
                    select.innerHTML = '';

                var types = data[value];
                for(var i = 0; i < types.length; i++) {
                    var option = document.createElement("option");
                        option.value = types[i];
                        option.innerHTML = types[i];
                    select.appendChild(option);
                }

                select.disabled = false;

            }
        </script>
    </head>
    <body>

        <form method="POST">

            Filtr:
            <select onchange="filter(this.value, 'numbers', data)">
                <option value="vsechna">-- Vyberte filtr --</option>
                <option value="sude">Sudá</option>
                <option value="liche">Lichá</option>
                <option value="prvocisla">Prvočísla</option>
            </select>

            Čísla:
            <select id="numbers">
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
                <option value="6">6</option>
                <option value="7">7</option>
                <option value="8">8</option>
            </select>

        </form>

    </body>
</html>

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0