Disponibilità camere

Rey

Utente Attivo
11 Set 2012
73
0
6
Buon giorno a tutti,
dovrei controllare la disponibilità di camere in un booking online, questa è la tabella relativa alle prenotazioni 2015:

idprenota-idclienti-idappartamenti-iddatainizio-iddatafine
1-1-01-34-34
2-2-02-34-35

Come potete vedere le date vengono salvate con il giorno dell'anno (prenotazione 1 la notte del 3 febbraio, prenotazione 2 la notte del 3 e 4 febbraio).

Mettendo il caso di avere 5 appartamenti (3 tipologie):

01 Piccolo
02 Medio
03 Medio
04 Medio
05 Grande

Come posso controllare se tipo dal 02/02/2015 al 04/02/2015 è disponibile un appartamento medio? Ho provato in php a fare qualcosa di analogo ma venivano un sacco di cicli.
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
supponendo che tu abbia,

tabella appartamenti :
idappartamenti-tipo
01-Piccolo
02-Medio
03-Medio
04-Medio
05-Grande

tabella prenotazioni :
idprenota-idclienti-idappartamenti-iddatainizio-iddatafine
1-1-01-34-34
2-2-02-34-35

potresti provare questa select composta che (dovrebbe) estrarre l'elenco degli appartamenti liberi tra le date
$date_in e $date_out, queste sono le variabili php da passare alla query

volendo puoi aggiungere una condizione where sulla select più esterna selezionando la tipologia voluta

il problema nasce al cambiare dell'anno perchè (mi sembra) non è presente nella prenotazione

Codice:
SELECT * from appartamenti
WHERE NOT EXISTS
(
SELECT idappartamenti 
FROM prenotazioni
WHERE
(
( iddatainizio >= $date_in AND iddatainizio <= $date_out ) 
OR
( iddatafine > $date_in AND iddatafine <= $date_out ) 
OR
( $date_in >= iddatainizio AND $date_in < iddatafine ) 
)
AND idappartamenti=appartamenti.idappartamenti
)
ps
34-34 entra il 34 ed esce il 35 perché il 35 mattina l'appartamento è occupato
34-35 entra il 34 ed esce il 36 idem come sopra per il 36
 
Ultima modifica:

Rey

Utente Attivo
11 Set 2012
73
0
6
ciao marino, grazie per l'aiuto però non funziona mi restituisce sempre database vuoto..

PHP:
$idcamera = 02;
$checkin = "2015-02-04";
$checkout = "2015-02-05";


$query = "SELECT idappartamenti FROM " . $prefixdb . "prenota2015
 WHERE 
 (
 (iddatainizio >= DAYOFYEAR($checkin) AND iddatainizio <= DAYOFYEAR($checkout))
 OR
 (iddatafine > DAYOFYEAR($checkin) AND iddatafine <= DAYOFYEAR($checkout))
 OR
 (DAYOFYEAR($checkin) >= iddatainizio AND DAYOFYEAR($checkout) < iddatainizio)
 ) AND idappartamenti = $idcamera";
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
ciao marino, grazie per l'aiuto però non funziona mi restituisce sempre database vuoto..
nella mia query ci sono due select, nella tua una sola
tu hai usato la select più interna che restituisce gli appartamenti occupati
mentre quella esterna, restituisce quelli liberi
prova a capire come funziona la query ...
 

Rey

Utente Attivo
11 Set 2012
73
0
6
Ho provato a copiare la query pari pari, mi da sempre disponibile l'appartamento 1 piccolo anche se è tutto occupato o tutto libero..

PHP:
$query = "SELECT * from apt
WHERE NOT EXISTS
(
SELECT idappartamenti 
FROM " . $prefixdb . "prenota2015
WHERE
(
( iddatainizio >= DAYOFYEAR($checkin) AND iddatainizio <= DAYOFYEAR($checkout)) 
OR
( iddatafine > DAYOFYEAR($checkin) AND iddatafine <= DAYOFYEAR($checkout)) 
OR
( DAYOFYEAR($checkin) >= iddatainizio AND DAYOFYEAR($checkin) < iddatafine ) 
)
AND idappartamenti=apt.idappartamenti
)";

$result = mysql_query($query, $myconn) or die(mysql_error());

$numrows = mysql_num_rows($result);
$resrow = mysql_fetch_row($result);
// Se il database è vuoto lo stampo a video
if ($numrows==0){ echo "Database vuoto!"; 
}
// Se invece trovo delle occorrenze...
else
{

  foreach($resrow as $testo){

echo $testo;
echo "<br>";
}
}
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
queste sono le mie date nel db
Codice:
insert into prenotazioni values (1, datepart(dy, '2015/10/09'), datepart(dy, '2015/10/11'), 'W')
insert into prenotazioni values (2, datepart(dy, '2015/10/10'), datepart(dy, '2015/10/12'), 'W')
insert into prenotazioni values (3, datepart(dy, '2015/10/11'), datepart(dy, '2015/10/13'), 'W')

insert into prenotazioni values (3, datepart(dy, '2015/10/16'), datepart(dy, '2015/10/17'), 'W')

insert into prenotazioni values (3, datepart(dy, '2015/10/20'), datepart(dy, '2015/10/20'), 'W')

questa è il mio script per il test
Codice:
declare @idappartamenti	as int
declare @checkin	as int
declare @checkout	as int

set @checkin  = datepart(dy, '2015/10/08');
set @checkout = datepart(dy, '2015/10/15');

SELECT @checkin as iddatainizio, @checkout as iddatafine

SELECT 1 as seltype, * FROM prenotazioni WHERE iddatainizio >= @checkin AND iddatainizio <= @checkout
UNION
SELECT 2 as seltype, * FROM prenotazioni WHERE iddatafine > @checkin AND iddatafine <= @checkout

SELECT * from appartamenti 
WHERE NOT EXISTS
(
SELECT idappartamenti 
FROM prenotazioni
WHERE
(
( iddatainizio >= @checkin AND iddatainizio <= @checkout ) 
OR
( iddatafine > @checkin AND iddatafine <= @checkout ) 
OR
( @checkin >= iddatainizio AND @checkin < iddatafine ) 
)
AND idappartamenti=appartamenti.idappartamenti
)
questo è il risultato,
nella prima sezione, le date di ricerca
nella seconda, gli appartamenti occupati, con i giorni impegnati
nella terza, gli appartamenti liberi nel periodo di interesse (vedi la query che ti ho postato in precedenza)
MrWeb_appartamenti.PNG
 
Ultima modifica:

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
se consideri l'uscita al mattino e la possibilità di entrata al pomeriggio,
puoi eliminare un uguale "="
Codice:
SELECT * from appartamenti 
WHERE NOT EXISTS
(
SELECT idappartamenti 
FROM prenotazioni
WHERE
(
( iddatainizio >= @checkin AND iddatainizio < @checkout ) 
OR
( iddatafine > @checkin AND iddatafine <= @checkout ) 
OR
( @checkin >= iddatainizio AND @checkin < iddatafine ) 
)
AND idappartamenti=appartamenti.idappartamenti
)
 

Rey

Utente Attivo
11 Set 2012
73
0
6
Grazie per l'aiuto che mi stai dando, per te sarà banale ma non riesco a farlo funzionare.. Saresti così gentile da zipparmi il tuo script così lo provo direttamente?
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
non ho problemi a fare lo zip dello script, ma credo sia meglio fare qualche passo prima per verificare che tutto sia ben messo

per prima cosa ti costa poco modificare
PHP:
$checkin = "2015-02-04"; 
$checkout = "2015-02-05";
usando la funzione php e accertandoti del valore (var_dump) che viene assegnato
PHP:
$checkin =  1 + date('z', strtotime('2015-02-04'));
...
var_dump($checkin);
....
come seconda cosa sii sicuro che iddatainizio e iddatafine siano definiti come interi nel db
verificane il contenuto

poi modifica la query togliendo DAYOFYEAR (come vedi php vuole + 1)

ancora, posta i record presenti nella tua tabella prenotazioni

poi fatti vivo con le modifiche e verifiche
ciao
Marino
 
Ultima modifica:

Rey

Utente Attivo
11 Set 2012
73
0
6
Prenotazioni nel DB:

prenota.gif

Come vedi sono int:

prenota-int.gif

File:

PHP:
// Mi connetto e seleziono il db
include "test-conn.php";

$checkin =  1 + date('z', strtotime('2015-02-06'));

$checkout = 1 + date('z', strtotime('2015-02-07'));

var_dump($checkin,$checkout);

// Imposto ed eseguo la query

$query = "SELECT * from allyn_camere 
WHERE NOT EXISTS
(
SELECT idappartamenti 
FROM test_prenota2015
WHERE
(
( iddatainizio >= $checkin AND iddatainizio < $checkout ) 
OR
( iddatafine > $checkin AND iddatafine <= $checkout ) 
OR
( $checkin >= iddatainizio AND $checkin < iddatafine ) 
)
AND idappartamenti=allyn_camere.idappartamenti
)";


$result = mysql_query($query, $myconn) or die(mysql_error());

// Conto il numero di occorrenze trovate nel db
$numrows = mysql_num_rows($result);
$resrow = mysql_fetch_row($result);
// Se il database è vuoto lo stampo a video
if ($numrows==0){ echo "Database vuoto!"; 
}
// Se invece trovo delle occorrenze...
else
{
  // Avvio un ciclo for che si ripete per il numero di occorrenze trovate
  for($p=0; $p<$numrows; $p++){
  	$uno = $resrow[0];
	$due = $resrow[1];

echo $uno . ' ' . $due;
echo "<br>";

	

}
}

mysql_close($myconn);
Output:
PHP:
int(37) int(38) 1 Piccolo
1 Piccolo
1 Piccolo
1 Piccolo
1 Piccolo
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
maybe ... maybe ...
ho inserito i tuoi record nel database ed ho eseguito lo script .... funziona,

ma .... guardando il tuo codice,
fai girare un indice "p" ma poi "peschi" sempre 0 e 1 .....
PHP:
for($p=0; $p<$numrows; $p++){ 
  $uno = $resrow[0]; 
  $due = $resrow[1];
 

Rey

Utente Attivo
11 Set 2012
73
0
6
Se metto:
PHP:
  // Avvio un ciclo for che si ripete per il numero di occorrenze trovate
  for($p=0; $p<$numrows; $p++){
  	$uno = $resrow[0];
	$due = $resrow[1];
	$tre = $resrow[2];

echo $uno . ' ' . $due . ' ' . $tre;
echo "<br>";
PHP:
int(37) int(38)
Notice: Undefined offset: 2 in E:\xxx\test.php on line 60
1 Piccolo

Notice: Undefined offset: 2 in E:\xxx\test.php on line 60
1 Piccolo

Notice: Undefined offset: 2 in E:\xxx\test.php on line 60
1 Piccolo

Notice: Undefined offset: 2 in E:\xxx\test.php on line 60
1 Piccolo

Notice: Undefined offset: 2 in E:\xxx\test.php on line 60
1 Piccolo
Se invece metto:
PHP:
  // Avvio un ciclo for che si ripete per il numero di occorrenze trovate
  foreach($resrow as $testo){
 

echo $testo;
echo "<br>";
PHP:
int(37) int(38) 1
Piccolo
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
in ogni caso il contatore conta 5 righe (anche se il tuo codice stampa solo "1 piccolo")
io non conosco bene mysql perché lavoro con ms sql ma ci provo lo stesso a suggerire una variazione,
PHP:
// Conto il numero di occorrenze trovate nel db 
$numrows = mysql_num_rows($result);

// Se il database è vuoto lo stampo a video 
if ($numrows==0) { echo "Database vuoto!"; }

// Se invece trovo delle occorrenze... 
else 
{ 
  // Avvio un ciclo per la stampa
  while($row = mysql_fetch_array($result)){
    $uno = $row[0]; 
    $due = $row[1];
    echo $uno . ' ' . $due; 
    echo "<br>"; 
}
può essere che invece di 0 e 1 si debba mettere il nome del campo del db

prova anche così
PHP:
for($p=0; $p<$numrows; $p++){ 
    $uno = $resrow[p][0]; 
    $due = $resrow[p][1];
 
Ultima modifica:

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
un ultimo suggerimento, ho trovato organizzazioni (tedesche) che, il giorno di uscita lo considerano impegnato,
per via delle pulizie

quindi fai dei test, applicando ai simboli maggiore e minore delle query il simbolo di uguale, per essere sicuro
che diano l'occupato secondo le attese

quello che segue è lo script che identifica gli appartamenti/camere occupate,
vedi le 3 select applicate ed identificate da 1, 2 e 3
quindi puoi anche individuare la select che ti da il risultato di occupato

puoi inserire un appartamento solo con date di prenotazione e far scorrere le date checkin e checkout
per valutarne il risultato

Codice:
declare @idappartamenti	as int
declare @checkin	as int
declare @checkout	as int

set @checkin  = datepart(dy, '2015/02/06');
set @checkout = datepart(dy, '2015/02/07');

SELECT @checkin as iddatainizio, @checkout as iddatafine

SELECT 1 as seltype, * FROM prenotazioni WHERE iddatainizio >= @checkin AND iddatainizio < @checkout
UNION
SELECT 2 as seltype, * FROM prenotazioni WHERE iddatafine > @checkin AND iddatafine <= @checkout
UNION
SELECT 3 as seltype, * FROM prenotazioni WHERE @checkin >= iddatainizio AND @checkin < iddatafine
adattalo a mysql
ciao
Marino
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
scusa, ancora qualche riflessione, pur non conoscendo la tua applicazione ed il caso specifico,

non so chi/cosa ti costringa a memorizzare il giorno anzichè la data, qualche byte risparmiato ?
con le date puoi usare tutte le funzioni di calcolo, con i giorni devi fare "giri" di programmazione
esempio tra 2015-12-31 e 2016-01-01 c'è un giorno ma tra 365(366) e 1 ??

preferisco avere sempre il dato "consistente" memorizzato (yyyy-mm-gg),
salvo rappresentarlo a video nel formato che l'utilizzatore desidera

perchè avere le prenotazioni in una tabella annuale (supponendo di avere la data consistente) ?
di nuovo complicazione nella programmazione per gestire periodi tra anni
sarebbe meglio avere le prenotazioni "correnti" in una tabella, che mantieni "pulita" con un colpo di query
(che suppongo funzioni anche in mysql),
Codice:
DELETE FROM prenotazioni
OUTPUT DELETED.*
INTO PrenotazioniSave
WHERE iddatafine<'2014-12-99' // 99 voluto (ma puoi anche parzializzare l'anno corrente)
con la situazione delle prenotazioni correnti e le salvate,
puoi crearti una view che restituisca la select su entrambe le tabelle da usare per eventuali statistiche

da buon ultimo non dimenticare di creare un cliente "manutenzione", per impegnare l'appartamento quando necessario

poi viene la formazione del prezzo con ... le sue date

come vedi ... poche idee e ben confuse, le mie
ma cerco di avere anche la "vita facile" (suggerimento più importante)
ciao
Marino
 

Rey

Utente Attivo
11 Set 2012
73
0
6
un ultimo suggerimento, ho trovato organizzazioni (tedesche) che, il giorno di uscita lo considerano impegnato,
per via delle pulizie
Nel mio caso il checkout è alle 10:00, alle 12:00 le pulizie sono finite e la camera è rioccupabile.

quindi fai dei test, applicando ai simboli maggiore e minore delle query il simbolo di uguale, per essere sicuro
che diano l'occupato secondo le attese
Ho levato l'= come mi hai suggerito qui http://forum.mrwebmaster.it/mysql/42164-disponibilit-camere.html#post165424
sembra funzionare

quello che segue è lo script che identifica gli appartamenti/camere occupate,
vedi le 3 select applicate ed identificate da 1, 2 e 3
quindi puoi anche individuare la select che ti da il risultato di occupato

puoi inserire un appartamento solo con date di prenotazione e far scorrere le date checkin e checkout
per valutarne il risultato

Codice:
declare @idappartamenti	as int
declare @checkin	as int
declare @checkout	as int

set @checkin  = datepart(dy, '2015/02/06');
set @checkout = datepart(dy, '2015/02/07');

SELECT @checkin as iddatainizio, @checkout as iddatafine

SELECT 1 as seltype, * FROM prenotazioni WHERE iddatainizio >= @checkin AND iddatainizio < @checkout
UNION
SELECT 2 as seltype, * FROM prenotazioni WHERE iddatafine > @checkin AND iddatafine <= @checkout
UNION
SELECT 3 as seltype, * FROM prenotazioni WHERE @checkin >= iddatainizio AND @checkin < iddatafine
adattalo a mysql
ciao
Marino
Codice:

PHP:
$query = "SELECT 1 as seltype, * FROM test_prenota2015 WHERE iddatainizio >= $checkin AND iddatainizio < $checkout
UNION
SELECT 2 as seltype, * FROM test_prenota2015 WHERE iddatafine > $checkin AND iddatafine <= $checkout
UNION
SELECT 3 as seltype, * FROM test_prenota2015 WHERE $checkin >= iddatainizio AND $checkin < iddatafine";


$result = mysql_query($query, $myconn) or die(mysql_error());
//echo $query;
// Conto il numero di occorrenze trovate nel db
// Conto il numero di occorrenze trovate nel db 
$numrows = mysql_num_rows($result);

// Se il database è vuoto lo stampo a video 
if ($numrows==0) { echo "Database vuoto!"; }

// Se invece trovo delle occorrenze... 
else 
{ 
  // Avvio un ciclo per la stampa
  while($row = mysql_fetch_array($result)){
    $uno = $row[0]; 
    $due = $row[1];
    echo $uno . ' ' . $due; 
    echo "<br>"; 
}  
}

mysql_close($myconn);
Output
Codice:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '* FROM test_prenota2015 WHERE iddatainizio >= 38 AND iddatainizio < 42 UNION S' at line 1
scusa, ancora qualche riflessione, pur non conoscendo la tua applicazione ed il caso specifico,

non so chi/cosa ti costringa a memorizzare il giorno anzichè la data, qualche byte risparmiato ?
con le date puoi usare tutte le funzioni di calcolo, con i giorni devi fare "giri" di programmazione
esempio tra 2015-12-31 e 2016-01-01 c'è un giorno ma tra 365(366) e 1 ??
Non ho scritto io questo programma di prenotazioni, è opensorce scritto in php (hoteldruid) funziona davvero bene e salva le date in questo modo, tabelle che visualizza le camere, statistiche, tariffe etc.. ha già tutto, quello che sto cercando di fare io è un controllo della disponibilità in modo che la gente possa prenotare online :) Fortunatamente a dicembre-gennaio siamo chiusi quindi un grattacapo in meno :D
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
guardando il manuale mysql, le select in "union" sembra siano racchiuse tra parentesi
Codice:
$query = "(SELECT 1 as seltype, * FROM test_prenota2015 WHERE iddatainizio >= $checkin AND iddatainizio < $checkout) 
UNION 
(SELECT 2 as seltype, * FROM test_prenota2015 WHERE iddatafine > $checkin AND iddatafine <= $checkout) 
UNION 
(SELECT 3 as seltype, * FROM test_prenota2015 WHERE $checkin >= iddatainizio AND $checkin < iddatafine)";
ciao
Marino
 

Rey

Utente Attivo
11 Set 2012
73
0
6
Da sempre l'errore ma non ha importanza, vorrei mi dessi un'ultima dritta sul maggiore e minore per considerare il giorno di uscita libero, allo stato attuale:

PHP:
$query = "SELECT * from allyn_camere 
WHERE NOT EXISTS
(
SELECT idappartamenti 
FROM test_prenota2015
WHERE
(
( iddatainizio >= $checkin AND iddatainizio < $checkout ) 
OR
( iddatafine > $checkin AND iddatafine <= $checkout ) 
OR
( $checkin >= iddatainizio AND $checkin < iddatafine ) 
)
AND idappartamenti=allyn_camere.idappartamenti
)";
Mettendo il caso di avere tutte le camere occupate dal 34 al 36 ovvero la notte del 3, 4 e 5 febbraio, se cerco disponibilità il 5 febbraio mi da tutto libero!

Bisogna fare attenzione perchè se inseriamo una prenotazione nel programma verranno salvate le NOTTI occupate quindi ad esempio gli id di soggiorni di una notte coicideranno. ESEMPIO:

Prenotazione dal 3 (checkin) al 4 (checkout) febbraio:

iddatainizio = 34
iddatafine = 34

Controllo disponibilità dal 3 al 4 febbraio:

checkin = 34
checkout = 35

E' tutto il giorno che faccio prove e mi si sta intrippando il cervello.. Secondo te è il caso di modificare:

PHP:
$checkin =  1 + date('z', strtotime('2015-02-05'));
$checkout = 1 + date('z', strtotime('2015-02-06'));
con

PHP:
$checkin =  1 + date('z', strtotime('2015-02-05'));
$checkout = 0 + date('z', strtotime('2015-02-06'));
per avere tutto uguale?
 

marino51

Utente Attivo
28 Feb 2013
2.731
141
63
Lombardia
Da sempre l'errore ma non ha importanza
in realtà serve per i test ..., in ogni caso verifica i test rappresentati dalle figure allegate nello zip
Vedi l'allegato DispoCamere2.zip
questa è la query che ha permesso di ottenerli
Codice:
SELECT * from appartamenti 
WHERE idappartamenti=1
and NOT EXISTS
(
SELECT idappartamenti 
FROM prenotazioni
WHERE
(
( iddatainizio >= @checkin AND iddatainizio <= @checkout ) 
OR
( iddatafine >= @checkin AND iddatafine <= @checkout ) 
OR
( @checkin >= iddatainizio AND @checkin < iddatafine ) 
)
AND idappartamenti=appartamenti.idappartamenti
)
per ogni riga, degli esempi, verifica data/ora ingresso e uscita visto, sia come camera prenotata, che come tentativo di prenotazione
se qualcosa non andasse bene, sarebbe utile vederlo come "disegno"
ciao
Marino