Oznámení
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ěnoVě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;
}
- toto zapíše a následně opravdu vytáhne ID_SUBJ
- SELECT je naprosto v pořádku, když si dám dibi::test tak je tak jak má být, ale nezapíše ho
- 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
}