tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

Kontrola křížení záznamů nefunguje spolehlivě

před 7 lety

Tirus91
Generous Backer | 199

Narazil jsem na problém při kontrole křížení záznamů.
Jde o to, že se nesmí křížit záznam pro dané dva identifikátory a jejich časové rozmezí. Ovšem jednou mi to vyhodnotí, že tam záznam je a někdy že tam není a to i přes to, že tam ten zázanam je.

$exists = (bool)dibi::query('SELECT [id_recu] FROM [:prefix:recu] WHERE %and', array('day_recu' => $recu['day_recu'], 'id_room_recu' => $recu['id_room_recu']), ' AND ( (%and', array(array('time_from_recu<=%s',
                    $recu['time_from_recu']), array('time_to_recu>=%s', $recu['time_from_recu'])), ') OR( %and', array(array('time_from_recu<=%s', $recu['time_to_recu']), array('time_to_recu>=%s', $recu['time_to_recu'])),
            '))')->fetchSingle();

výsledej je následný

SELECT `id_recu`
FROM `sk_recu`
WHERE (`day_recu` = '2012-07-04') AND (`id_room_recu` = '45') AND ( ((time_from_recu<='00:00:00')
AND (time_to_recu>='00:00:00') ) OR( (time_from_recu<='00:05:00') AND (time_to_recu>='00:05:00') ))

Dělám někde chybu? nebo se to dá dělat úplně jinak?

Editoval Tirus91 (29. 7. 2012 21:49)

před 7 lety

HosipLan
Moderator | 4693

Ano, mám pocit, že tohle není to co potřebuješ.

http://www.linuxsoft.cz/article.php?… hledej „unique“

před 7 lety

Tirus91
Generous Backer | 199

HosipLan napsal(a):

Ano, mám pocit, že tohle není to co potřebuješ.

http://www.linuxsoft.cz/article.php?… hledej „unique“

Cože? UNIQUE s mojím problémem nijak nesouvisí … tam může být X záznamů pro id_room_recu apod… jde mi jen o to, že když to má stejné identifikátory, tak se nesmí křížit časy

před 7 lety

LuKo
Člen | 114

Ještě ti tam chybí případ, kdy je záznam uvnitř intervalu:

OR (time_from_recu>='00:00:00') AND (time_to_recu<='00:05:00')

Jinak to lze zjednodušit na:

SELECT `id_recu`
FROM `sk_recu`
WHERE (`day_recu` = '2012-07-04') AND (`id_room_recu` = '45') AND (`time_to_recu` >= '00:00:00') AND (`time_from_recu` <= '00:05:00')

před 7 lety

Tirus91
Generous Backer | 199

LuKo napsal(a):

Ještě ti tam chybí případ, kdy je záznam uvnitř intervalu:

OR (time_from_recu>='00:00:00') AND (time_to_recu<='00:05:00')

Jinak to lze zjednodušit na:

SELECT `id_recu`
FROM `sk_recu`
WHERE (`day_recu` = '2012-07-04') AND (`id_room_recu` = '45') AND (`time_to_recu` >= '00:00:00') AND (`time_from_recu` <= '00:05:00')

No vidíš že jsem na to přišel sám :) .. Nee, kecám.. díky. Večer se mi to snad podaří zkusit. Ovšem příde mi tam chybka ne?

co když time_to_recu není větší, ale je větší time_from_recu .. v tu chvíli to není splněno

Editoval Tirus91 (30. 7. 2012 17:30)

před 7 lety

LuKo
Člen | 114

Tirus91 napsal(a):
co když time_to_recu není větší, ale je větší time_from_recu .. v tu chvíli to není splněno

Větší, než co?

před 7 lety

Tirus91
Generous Backer | 199

LuKo napsal(a):

Tirus91 napsal(a):
co když time_to_recu není větší, ale je větší time_from_recu .. v tu chvíli to není splněno

Větší, než co?

Teď nějak nevím…
Udělal jsem to jak jsi psal, ovšem netváří se to nějak funkčně.

private function writeRecu($recu)    {
        $array = array(
            'id_rent_recu',
            'id_room_recu',
            'id_user_recu',
            'time_from_recu',
            'time_to_recu',
            'day_recu');
        foreach ($recu as $col => $val) {
            if (!in_array($col, $array)) {
                unset($recu[$col]);
            }
        }
        if(strlen($recu['time_to_recu'])==5){
                $recu['time_to_recu'].=':00';
        }
        if(strlen($recu['time_from_recu'])==5){
                 $recu['time_from_recu'].=':00';
        }
        if (strtotime($recu['day_recu']) == false) {
            return false;
        }
        $day = new DateTime($recu['day_recu']);
        $recu['day_recu'] = $day->format('Y-m-d');
        $recu['id_user_recu'] = $this->registry['sk_user']->user_info['id_user'];
        $cross = array();
         $exists = dibi::query('SELECT [id_recu] FROM [:prefix:recu] WHERE %and', array(
            'day_recu' => $recu['day_recu'],
            'id_room_recu' => $recu['id_room_recu'],
            array('time_to_recu >=%t', $recu['time_from_recu']),
            array('time_from_recu<=%t', $recu['time_to_recu'])))->fetchSingle();// ad3

        if ($exists!== false) {
            $this->error = 'Pronájem se kříží s jiným.';
            return false;
        }
        dibi::begin();
        try {
            dibi::query('INSERT INTO [:prefix:subj] ', array('type_subj' => SUBJ_RECU));  // ad1
            $recu['id_recu'] = dibi::getInsertId();
            dibi::query('INSERT INTO [:prefix:recu] ', $recu);                // ad2
            dibi::commit();
            return true;
        }
        catch (DibiException $e) {
            var_export($e);
            dibi::rollback();
        }
        return false;
    }
  1. toto zapíše a následně opravdu vytáhne ID_SUBJ
  2. SELECT je naprosto v pořádku, když si dám dibi::test tak je tak jak má být, ale nezapíše ho
  3. toto je neustále stejný problém. Jednou mi to zahlásí že se kříží a někdy ne.

$recu = array(7) { [„time_from_recu“]⇒ string(5) „12:03“ [„time_to_recu“]⇒ string(5) „23:00“ [„date_from_recu“]⇒ string(10) „31.07.2012“ [„date_to_recu“]⇒ string(0) "" [„id_rent_recu“]⇒ string(3) „104“ [„id_room_recu“]⇒ string(3) „103“ [„day_recu“]⇒ string(10) „31.07.2012“ }

a var_export($e); nevypíše nikdy nic

Editoval Tirus91 (31. 7. 2012 17:32)

před 7 lety

Milo
Nette Core | 1119

Zkrácená podmínka jak psal Luko by měla fungovat. Pokud je zajištěno, že $from < $to tedy…

$day = '2012-07-04';
$idRoomRecu = 45;
$from = '00:00:00';
$to = '00:05:00';

$count = dibi::query('
    SELECT
        COUNT(*)
    FROM
        sk_recu
    WHERE
        day_recu = %d', $day, '
        AND
        id_room_recu = %i', $idRoomRecu, '
        AND
        time_from_recu > %s', $to, '
        AND
        time_to_recu < %s', $from, '
')->fetchSingle();

if ($count > 0) {
    // chyba
}

Pokud ale chceš zajistit, že na 100% nebudeš mít v databázi překrývající se intervaly, měl bys to řešit v principu jak psal HosipLan. Takhle není funkce writeRecu() vícevláknově bezpečná.

Pokud to MySQL umí (myslím, že neumí) použij nějaký UNIQUE index na krytí intervalů. Nebo tabulku uzamykej pro zápis během celé funkce. Nebo řádek jednoduše vlož, ověř že je jedinečný a pokud ano, commit, pokud ne, rollback a chyba.

// V pseudokódu
dibi::begin()
dibi::insert('nový řádek');
$cnt = dibi::select('COUNT() ... podmínka na jedinečnost')->fetchSingle();
if ($cnt === 1) {
    dibi::commit();
} else {
    dibi::rollback();
    // chyba
}