Autor Zpráva
inen
Profil *
Ahoj,

chtěl bych poprosit o radu s dotazem na databázi. Script jako takový funguje jen ho potřebuji omezit, aby načítal každou 30 položku z databáze. Napadlo mě dát tam podmínku s počítadlem, ale není něco jednoduššího? Nic jsem nenalezl.

Děkuji za radu.


<?php

$url = 'www.xxx.com';
$obj = json_decode(file_get_contents($url), true);

//db
$conn = new mysqli("localhost", "root", "", "aa");

// Stochastic K index
$Stochastic_k_15m = array();

// výběr posledních 3000 položek z databaze, ale kazde 30 polozky podle ID a casu
$result = $conn->query("SELECT id, value FROM price ORDER BY id DESC LIMIT 3000");

    // začnu načítat 3000 položek z databáze po jednotlivých řádcích
    while($row = $result->fetch_assoc()) {

        
            // Stochastic K
            array_push($Stochastic_k_15m, $row["value"]);            

        
    }

// Nacteni nejvyssi a nejnizsi hodnoty z pole
$Stochastic_k_Hx_15 = max($Stochastic_k_15m);
$Stochastic_k_Lx_15 = min($Stochastic_k_15m);

$Stochastic_k_index15_vypocet = 100*(($obj['lastPrice']-$Stochastic_k_Lx_15)/($Stochastic_k_Hx_15-$Stochastic_k_Lx_15));

echo "Stochastic vysledek: ".$Stochastic_k_index15_vypocet;

?>
Kajman
Profil
Ve starších verzích mysql lze použít např. uživatelské proměnné...
SET @counter=0;
SELECT id, value, @counter rownum FROM price HAVING (@counter:=@counter+1)%30=0 ORDER BY id DESC LIMIT 100

U novějších verzí mariadb by šly použít např. window funkce.
inen
Profil *
Děkuji za odpověď, ale nějak mi to nechce fungovat. Měl bych ještě dotaz z trochu jiného směru.

Každých 30 sekund načítám z api hodnoty a uložím do databáze.

ID / Value / Date / Time

Chtěl bych provést výběr z databáze podle času/id, tak aby to načetlo vždy jen hodnoty uložené každou:

a) 1 minutu (tzn. 00:01, 00:02, 00:03,...) za posledních 14 dnů, aby ignoroval hodnoty mezi tím
b) 15 minut (...) za posledních 14 dnů, aby ignoroval hodnoty mezi tím
c) 1 hodinu (...) za posledních 14 dnů, aby ignoroval hodnoty mezi tím
d) 24 hodin (...) za posledních 14 dnů, aby ignoroval hodnoty mezi tím

tzn. vždy vznikne array (); které bude obsahovat jen požadované hodnoty.

A to zpětně vždy 14 dnů od aktuálního času. Už pár dnů nad tím přemýšlím, ale zatím bez úspěchu. Prosím o radu.

Děkuji za každou radu.
Kajman
Profil
Chcete, aby to bylo svižné? Máte možnost těch window funkcí?
inen
Profil *
Aktuálně jedu na starším PC s Windows 10 a na něm je nainstalovaný WAMP server.

Window Functions bohužel vůbec neznám.

Čím svižnější tím lepší :-)
Keeehi
Profil
inen:
Window Functions bohužel vůbec neznám.
Nejde o to, zda je znáš ty ale zda je zná databáze. MySQL je zná od verze 8.0.2.

Ty body a) - d) se provádějí v jednom a tom samém běhu scriptu? Jestli ano, pak žádný problém nemáš. Vybereš data pro a), což je obyčejný select všech záznamů za posledních 14 dní. To zvládne snad každá databáze. No a b) - d) jsou jen podmnožiny a). Jelikož pole pro a) už máš v paměti, stačí ti ho jen projít po správných indexech.
A jen tak mimochodem, 20000 záznamů není až tak velké číslo a najít minimum a maximum v poli nebude tedy až tak velký problém. Přesto není potřeba toto pole vůbec vytvářet, protože minimum a maximum jde najít už v tom cyklu kde to pole vytváříš.

<?php
$url = 'www.xxx.com';
$obj = json_decode(file_get_contents($url), true);
 
//db
$conn = new mysqli("localhost", "root", "", "aa");
 
// Stochastic K index
$Stochastic_k_15m = array();

$intervals = [1, 15, 60, 1440];

$min = array_fill(0, count($intervals), PHP_INT_MAX);
$max = array_fill(0, count($intervals), PHP_INT_MIN);
 
// výběr posledních 3000 položek z databaze, ale kazde 30 polozky podle ID a casu
$result = $conn->query("SELECT value FROM price ORDER BY id DESC LIMIT 20160");

for($i = 0; $row = $result->fetch_assoc(); $i++) {
    foreach($intervals as $index => $interval ) {
        if($i%$interval === 0) {
            if($row['value'] < $min[$index]) {
                $min[$index] = $row['value'];
            }
            if($row['value'] > $max[$index]) {
                $max[$index] = $row['value'];
            }
        }
    }
}

foreach($intervals as $index => $interval ) {
    echo "Stochastic výsledek pro interval $interval minut: " . 100*(($obj['lastPrice']-$min[$index])/($max[$index]-$min[$index])) . "<br>\n";
}
?>

Pokud ale ty window funkce máš, bude pak nejspíše nejlepší varianta nechat minimum a maximum spočítat už databázi.
inen
Profil *
MySQL mám verzi 5.7.21

Na druhou stranu není problém nainstalovat alternativu WAMP serveru. Doporučíš něco co je stejně "jednoduchého"?

a) - d) Ano bude v rámci jednoho scriptu v tom samém běhu.

Pokud budu chtít výsledky za minutu, tak v tom nevidím problém. Jednoduchý Select pro výběr 20 000 posledních položek z databáze.

Jak, ale rozlišit ty podmnožiny? Dejme tomu 15-ti minutový interval.
Původně jsem to chtěl udělat tak, že načtu každou 15. položku z databáze. Tam, ale nastává problém, že pokud dám SELECT databáze v 16:20, tak mi to bude načítat data zpětně v 16:05, 15:50, 15:35, 15:20,...
Já bych, ale potřeboval záznamy z 16:00, 15:45, 15:30, 15:15,...

Zatím mě, vůbec nenapadá jak je indexovat.
inen
Profil *
Ještě mě napadlo dát do scriptu co vkládá hodnoty do databáze něco takového - pro rozlišení:

<?php

// kazdych 15 minut
if (in_array(StrFTime("%M", Time()), array(00, 15, 30, 45))){ echo "Do databáze -> Sloupec 15minut -> Y";    }
    
// kazdou hodinu    
if (in_array(StrFTime("%H:%M", Time()), array("00:00","01:00","02:00","03:00","04:00","05:00","06:00","07:00","08:00","09:00","10:00","11:00","12:00","13:00","14:00","15:00","16:00","17:00","18:00","19:00","20:00","21:00","22:00","23:00"))){  echo "Do databáze -> Sloupec 1hodina -> Y"; }
    
// jednou za 24 hodin
if (StrFTime("%H", Time())  == 00){ echo "Do databáze -> Sloupec 24hodin -> Y";    }

?>
Kajman
Profil
Zkuste toto, zda je to dostatečně rychlé...

SELECT t.*,
       p2.value first_value
FROM   (SELECT intervaly.sekund,
               intervaly.popis,
               p.date,
               Min(p.time)  min_time,
               Min(p.value) min_value,
               Max(p.value) max_value,
               Avg(p.value) avg_value
        FROM   price p
               CROSS JOIN (SELECT 'minuty' popis,
                                  60       sekund
                           FROM   DUAL
                           UNION ALL
                           SELECT 'ctvrthodiny' popis,
                                  15 * 60       sekund
                           FROM   DUAL
                           UNION ALL
                           SELECT 'hodiny' popis,
                                  60 * 60  sekund
                           FROM   DUAL
                           UNION ALL
                           SELECT 'dny'        popis,
                                  24 * 60 * 60 sekund
                           FROM   DUAL) intervaly
        WHERE  p.date BETWEEN Subdate('2018-05-08', 13) AND Date('2018-05-08')
        GROUP  BY intervaly.popis,
                  intervaly.sekund,
                  p.date,
                  Time_to_sec(p.time) DIV intervaly.sekund) t
       JOIN price p2
         ON t.date = p2.date
            AND t.min_time = p2.time
ORDER  BY t.sekund DESC,
          t.date,
          t.min_time  
Keeehi
Profil
inen:
Tam, ale nastává problém, že pokud dám SELECT databáze v 16:20, tak mi to bude načítat data zpětně v 16:05, 15:50, 15:35, 15:20,...
Já bych, ale potřeboval záznamy z 16:00, 15:45, 15:30, 15:15
To je ale velmi podstatná informace, kterou jsi nezmínil a v důsledku to úplně mění přístup k problému.

SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY ORDER BY date DESC) t;

SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY AND MINUTE(date) in (0, 15, 30, 45) ORDER BY date DESC) t;

SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY AND MINUTE(date) = 0 ORDER BY date DESC) t;

SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY AND HOUR(date) = 0 AND MINUTE(date) = 0 ORDER BY date DESC) t;

Mělo by to být snad dostatečně rychlé. Pokud ne, tak to bude testováním, těch určených intervalů. V takovém případě by se přistoupilo k denormalizaci právě skrz ty pomocné sloupce, jak tě to napadlo. Ale to jen v případě, že by to bylo pomalé. Jen bych ten výpočet pomocných sloupců nechal pak na MySQL.

A taky jsi nikde neuvedl, zda bereš 14 dní jako 20160 minut (což je trochu problém v období změny letního a zimního času) nebo chceš zažít vždy půlnocí 13 celých dní nazpět + ta část dne, co zatím dnes uběhla.
inen
Profil *
Děkuji mockrát. Nakonec jsem použil :

SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY ORDER BY date DESC) t;
 
SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY AND MINUTE(date) in (0, 15, 30, 45) ORDER BY date DESC) t;
 
SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY AND MINUTE(date) = 0 ORDER BY date DESC) t;
 
SELECT MIN(value), MAX(value)
FROM (SELECT value FROM price WHERE date > NOW() - INTERVAL 14 DAY AND HOUR(date) = 0 AND MINUTE(date) = 0 ORDER BY date DESC) t;

A vše funguje. Snad tedy. Reálné výsledky se dozvím až budu mít 14 dnů dat.
Kajman
Profil
Pokud je sloupec date typu date a ne datetime (tedy tam je jen datum bez času jak píšete v [#3]), tak to nebude počítat dobře.

V takovém případě musíte ve funkcích minute a hour používat sloupec, kde je čas.

Další problém bude, že do vzorku se přidají např. všechny hodnoty z první minuty tedy 8:01:00 i 8:01:30. Možná by pomohlo přidat i podmínky, že sekundy z toho času jsou menší než 30.
inen
Profil *
Nakonec jsem omezil načítání z api/vkládání do databáze jednou za minutu. A to vždy v celou minutu. Stává se ale, že se to vloží do databáze např. 16:00:02, 16:01:01, 16:02:02,... To by, ale vliv mít nemělo, že?

Jinak ve sloupci date je i čas ve formátu: 2018-05-09 19:57:01

Takže by mělo být vše korektní, že?
Kajman
Profil
Ano, pokud nebude v minutě více záznamů a je to datetime, tak to bude fungovat.

Lze to i zkrátit. Order by je k ničemu a poddotaz také není potřeba. Pro test si můžete dát i count(*), abyste věděl, z kolika vzorků hodnot se hraniční hodnoty hledaly.

SELECT min(`value`),
       max(`value`)
FROM   `price`
WHERE  `date` > Now() - INTERVAL 14 day
       AND Minute(`date`) IN ( 0, 15, 30, 45 )
inen
Profil *
Děkuji za odpovědi. Tohle je tedy nejkratší možný zápis? Mám to správně? :-)

<?php

// připojení k databázi
$conn = new mysqli("localhost", "root", "", "");

$url = 'https://www.xxx.com/';
$obj = json_decode(file_get_contents($url), true);

// Výpočet Stochastic - K indexu
// Vzorec: %K = 100((C-Lx)/(Hx-Lx))
// Kde: Lx je nejnižší bod (low) za vybraný časový úsek, Hx je nejvyšší bod (high) za vybraný časový úsek, C je poslední zavírací cena (close) na příslušném časovém období (timeframu),

//Stochastic 1 minuta - 14 dnů
$Stochastic_K_1minuta = $conn->query("SELECT MIN(value), MAX(value) FROM price WHERE date > NOW() - INTERVAL 14 DAY")->fetch_assoc();
$Stochastic_K_1minuta_vypocet = round(100*(($obj['lastPrice']-$Stochastic_K_1minuta["MIN(value)"])/($Stochastic_K_1minuta["MAX(value)"]-$Stochastic_K_1minuta["MIN(value)"])), 1);

//Stochastic 15 minut - 14 dnů
$Stochastic_K_15minut = $conn->query("SELECT MIN(value), MAX(value) FROM price WHERE date > Now() - INTERVAL 14 day AND Minute(date) IN (0,15,30,45)")->fetch_assoc();
$Stochastic_K_15minut_vypocet = round(100*(($obj['lastPrice']-$Stochastic_K_15minut["MIN(value)"])/($Stochastic_K_15minut["MAX(value)"]-$Stochastic_K_15minut["MIN(value)"])), 1);

//Stochastic 1 hodina - 14 dnů

$Stochastic_K_1hodina =$conn->query("SELECT MIN(value), MAX(value) FROM price WHERE date > Now() - INTERVAL 14 DAY AND MINUTE(date) = 0 ")->fetch_assoc();
$Stochastic_K_1hodina_vypocet = round(100*(($obj['lastPrice']-$Stochastic_K_1hodina["MIN(value)"])/($Stochastic_K_1hodina["MAX(value)"]-$Stochastic_K_1hodina["MIN(value)"])), 1);

//Stochastic 1 den - 14 dnů
$Stochastic_K_1den = $conn->query("SELECT MIN(value), MAX(value) FROM price WHERE date > NOW() - INTERVAL 14 DAY AND HOUR(date) = 0 AND MINUTE(date) = 0")->fetch_assoc();
$Stochastic_K_1den_vypocet = round(100*(($obj['lastPrice']-$Stochastic_K_1den["MIN(value)"])/($Stochastic_K_1den["MAX(value)"]-$Stochastic_K_1den["MIN(value)"])), 1);

echo "Stochastic vysledek - 1 minuta: ".$Stochastic_K_1minuta_vypocet."<br>";
echo "Stochastic vysledek - 15 minut: ".$Stochastic_K_15minut_vypocet."<br>";
echo "Stochastic vysledek - 1 hodina: ".$Stochastic_K_1hodina_vypocet."<br>";
echo "Stochastic vysledek - 1 den: ".$Stochastic_K_1den_vypocet."<br>";

?>



//edit... Je tedy fakt, že za minutu a hodinu se mi teď vypisují stejné výsledky, takže tam asi mám chybu


/// edit2 - tak kecám - byla to jen náhoda několika minut... Teď už tam rozdíl je:

Stochastic vysledek - 1 minuta: 40.1
Stochastic vysledek - 15 minut: 40.6
Stochastic vysledek - 1 hodina: 40
Stochastic vysledek - 1 den: 15.8
Kajman
Profil
Zkrátit to lze tím, že nebudete stejné nebo podobné kusy kódu tapetovat, ale uděláte si alespoň pomocnou funkci, abyste případnou změnu dělal na jednom místě a ne na čtyřech. Tedy např. něco takového...

<?php
 
// připojení k databázi
$conn = new mysqli("localhost", "root", "", "");
 
$url = 'https://www.xxx.com/';
$obj = json_decode(file_get_contents($url), true);
 
// Výpočet Stochastic - K indexu
// Vzorec: %K = 100((C-Lx)/(Hx-Lx))
// Kde: Lx je nejnižší bod (low) za vybraný časový úsek, Hx je nejvyšší bod (high) za vybraný časový úsek, C je poslední zavírací cena (close) na příslušném časovém období (timeframu),
function stochastic_formula($C, $Lx, $Hx, $scale=1) {
    return round(100*(($C-$Lx)/($Hx-$Lx)), $scale);
}

function stochastic_from_db($conn, $C, $add_condition='', $days=14) {
    $data = $conn->query("SELECT MIN(value) minv, MAX(value) maxv FROM price WHERE date > Now() - INTERVAL {$days} day {$add_condition}")->fetch_assoc();
    return stochastic_formula($C, $data['minv'], $data['maxv']);
}

//Stochastic 1 minuta - 14 dnů
$Stochastic_K_1minuta_vypocet = stochastic_z_db($conn, $obj['lastPrice']);
//Stochastic 15 minut - 14 dnů
$Stochastic_K_15minut_vypocet = stochastic_z_db($conn, $obj['lastPrice'], "AND Minute(date) IN (0,15,30,45)");
//Stochastic 1 hodina - 14 dnů
$Stochastic_K_1hodina_vypocet = stochastic_z_db($conn, $obj['lastPrice'], "AND MINUTE(date) = 0");
//Stochastic 1 den - 14 dnů
$Stochastic_K_1den_vypocet = stochastic_z_db($conn, $obj['lastPrice'], "AND HOUR(date) = 0 AND MINUTE(date) = 0");
inen
Profil *
Děkuji za odpověď.
inen
Profil *
Ještě bych měl jeden dotaz.


Chci zobrazit poslední hodnoty - od aktuálního času 14 hodnot v rozmezí 15 minut

Zkoušel jsem tohle:
SELECT value FROM price WHERE date > Now() - INTERVAL 3.5 HOUR AND Minute(date) IN (0,15,30,45)

Zobrazí mi to ale 16 záznamů. Jak to? V jedné hodině 4 záznamy - tzn. 4 záznamy * 3 a půl hodiny = 14 záznamů. Proč tedy 16 ?

Děkuji za odpověď.


// edit vyřešil jsem to:

INTERVAL 210 MINUTE

Ale i tak, proč?
Kajman
Profil
Vypište si i date.
inen
Profil *
aha, hloupý dotaz :-)

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