Proponuję budować zapytanie przyrostowo, krok po kroku. Sprawdź, czy wyniki zapytania są zgodne z oczekiwaniami na każdym kroku. Jeśli coś „nie działa”, wykonaj kopię zapasową kroku.
Chcemy zwrócić trzy wiersze, po jednym dla każdego wiersza w ___Segmentations
, dla konkretnego hotelid
SELECT r.seg_id
, r.seg_text
FROM ___Segmentations r
WHERE r.seg_hotelid = :hotel_id
ORDER BY r.seg_id
Dodaj złącze zewnętrzne do __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
Dodaj sprzężenie zewnętrzne do ___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
Jeśli to są wiersze, które nas interesują, możemy popracować nad agregacją.
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
Teraz uzyskaj agregację z „suma”.
Podejście, które bym przyjął, polegałoby na wykonaniu „kopii” wierszy za pomocą operacji CROSS JOIN. Możemy wykonać sprzężenie z wierszami zwróconymi przez pierwsze zapytanie, które napisaliśmy, odwołując się do widoku wbudowanego. (Znaczony jako q
poniżej.)
Jeśli mamy pełny zestaw wierszy, powtórzonych dla każdego seg_id/seg_text
(pierwsze zapytanie, które napisaliśmy), możemy użyć agregacji warunkowej.
To ostatnie zapytanie, które napisaliśmy (tuż powyżej) jest widokiem wbudowanym w poniższym zapytaniu, aliasem c
.
SUMA cnt_bookings
ze wszystkich rzędów jest sumą.
W przypadku indywidualnych zliczeń możemy uwzględnić tylko wiersze, które mają pasujący seg_id
, łącznie tego podzbioru.
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
W SELECT
listy, możemy dokonać podziału, aby uzyskać procent:cnt_bookings * 100.0 / tot_bookings
np.
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
Zmodyfikuj klauzulę ORDER BY, aby zwracać wiersze w żądanej kolejności
Usuń z SELECT
wymień wyrażenia, które zwracają tot_bookings
i tot_billable
.
EDYTUJ
Chyba przegapiłem kryterium daty. Możemy przekształcić zewnętrzne łączenia w wewnętrzne i zastąpić CROSS JOIN LEFT JOIN. Mamy potencjał do zwrócenia wartości NULL dla cnt_bookings
i cnt_billable
, możemy owinąć je w funkcję IFNULL() lub COALESCE(), aby zastąpić NULL zerem.
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