Suggerisco di costruire la query in modo incrementale, passo dopo passo. Verificare che i risultati della query siano quelli previsti in ogni passaggio. Quando qualcosa "non funziona", esegui il backup di un passaggio.
Vogliamo restituire tre righe, una per ogni riga in ___Segmentations
, per un hotelid
specifico
SELECT r.seg_id
, r.seg_text
FROM ___Segmentations r
WHERE r.seg_hotelid = :hotel_id
ORDER BY r.seg_id
Aggiungi il join esterno a __Bookings
SELECT r.seg_id
, r.seg_text
, b.boo_id
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
WHERE r.seg_hotelid = :hotel_id
ORDER
BY r.seg_id
, b.boo_id
Aggiungi il join esterno a ___BillableDatas
SELECT r.seg_id
, r.seg_text
, b.boo_id
, d.bil_id
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
LEFT
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
WHERE r.seg_hotelid = :hotel_id
ORDER
BY r.seg_id
, b.boo_id
, d.bil_id
Se queste sono le righe che ci interessano, possiamo lavorare sull'aggregazione.
SELECT r.seg_id
, r.seg_text
, COUNT(DISTINCT b.boo_id) AS cnt_bookings
, COUNT(DISTINCT d.bil_id) AS cnt_billable
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
LEFT
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
WHERE r.seg_hotelid = :hotel_id
GROUP
BY r.seg_id
, r.seg_text
ORDER
BY r.seg_text
Ora per ottenere l'aggregazione con il "totale".
L'approccio che prenderei sarebbe quello di fare delle "copie" delle righe, usando un'operazione CROSS JOIN. Possiamo eseguire il join alle righe restituite dalla prima query che abbiamo scritto, a cui si fa riferimento come vista in linea. (Alias q
sotto.)
Se abbiamo un set completo di righe, ripetuto per ogni seg_id/seg_text
(quella prima query che abbiamo scritto), possiamo usare l'aggregazione condizionale.
L'ultima query che abbiamo scritto (appena sopra) è una vista in linea nella query sottostante, alias c
.
SOMMA di cnt_bookings
da tutte le righe è il totale.
Per i conteggi individuali, possiamo includere solo le righe che hanno un seg_id
corrispondente , un totale di quel sottoinsieme.
SELECT q.seg_id
, q.seg_text
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0)) AS cnt_bookings
, SUM(c.cnt_bookings) AS tot_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0)) AS cnt_billable
, SUM(c.cnt_billable) AS tot_billable
FROM ( SELECT t.seg_id
, t.seg_text
FROM ___Segmentations t
WHERE t.seg_hotelid = :hotel_id_1
ORDER BY t.seg_id
) q
CROSS
JOIN ( SELECT r.seg_id
, COUNT(DISTINCT b.boo_id) AS cnt_bookings
, COUNT(DISTINCT d.bil_id) AS cnt_billable
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
LEFT
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
WHERE r.seg_hotelid = :hotel_id
GROUP
BY r.seg_id
) c
GROUP
BY q.seg_id
, q.seg_text
ORDER
BY q.seg_text
Nel SELECT
list, possiamo fare la divisione per ottenere la percentuale:cnt_bookings * 100.0 / tot_bookings
es.
SELECT q.seg_id
, q.seg_text
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0)) AS cnt_bookings
, SUM(c.cnt_bookings) AS tot_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0))
* 100.0 / SUM(c.cnt_bookings) AS pct_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0)) AS cnt_billable
, SUM(c.cnt_billable) AS tot_billable
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0))
* 100.0 / SUM(c.cnt_billable) AS pct_billable
Modifica la clausola ORDER BY per restituire le righe nell'ordine desiderato
Rimuovi da SELECT
elenca le espressioni che restituiscono tot_bookings
e tot_billable
.
MODIFICA
Penso di aver perso i criteri della data. Possiamo trasformare le giunzioni esterne in giunzioni interne e sostituire il CROSS JOIN con un LEFT JOIN. Abbiamo il potenziale per restituire valori NULL per cnt_bookings
e cnt_billable
, possiamo racchiuderli nella funzione IFNULL() o COALESCE() per sostituire NULL con zero.
SELECT q.seg_id
, q.seg_text
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0)) AS cnt_bookings
, SUM(c.cnt_bookings) AS tot_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0))
* 100.0 / SUM(c.cnt_bookings) AS pct_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0)) AS cnt_billable
, SUM(c.cnt_billable) AS tot_billable
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0))
* 100.0 / SUM(c.cnt_billable) AS pct_billable
FROM ( SELECT t.seg_id
, t.seg_text
FROM ___Segmentations t
WHERE t.seg_hotelid = :hotel_id_1
ORDER BY t.seg_id
) q
LEFT
JOIN ( SELECT r.seg_id
, COUNT(DISTINCT b.boo_id) AS cnt_bookings
, COUNT(DISTINCT d.bil_id) AS cnt_billable
FROM ___Segmentations r
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
AND d.bil_date BETWEEN '2017-02-21' AND '2017-02-28'
WHERE r.seg_hotelid = :hotel_id
GROUP
BY r.seg_id
) c
ON 1=1
GROUP
BY q.seg_id
, q.seg_text
ORDER
BY q.seg_text