sql >> Base de Datos >  >> RDS >> Mysql

Devuelve una clasificación de varias tablas con mySQL

Sugiero que construyamos la consulta de forma incremental, paso a paso. Verifique que los resultados de la consulta sean los esperados en cada paso. Cuando algo "no funciona", haz una copia de seguridad de un paso.

Queremos devolver tres filas, una para cada fila en ___Segmentations , para un hotelid específico

 SELECT r.seg_id
      , r.seg_text
   FROM ___Segmentations r
  WHERE r.seg_hotelid = :hotel_id
  ORDER BY r.seg_id

Agregue la unión externa 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

Agregue la unión externa 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

Si esas son las filas que nos interesan, podemos trabajar en la agregación.

 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

Ahora para obtener la agregación con el "total".

El enfoque que tomaría sería hacer "copias" de las filas, usando una operación CROSS JOIN. Podemos unir las filas devueltas por la primera consulta que escribimos, referenciadas como una vista en línea. (Alias ​​como q a continuación.)

Si tenemos un conjunto completo de filas, repetido para cada seg_id/seg_text (esa primera consulta que escribimos), podemos usar la agregación condicional.

Esa última consulta que escribimos (justo arriba) es una vista en línea en la consulta a continuación, con el alias c .

SUMA de cnt_bookings de todas las filas es el total.

Para los recuentos individuales, podemos incluir solo las filas que tienen un seg_id coincidente , un total de ese subconjunto.

 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

En el SELECT list, podemos hacer la división para obtener el porcentaje:cnt_bookings * 100.0 / tot_bookings

por ejemplo

 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

Modifique la cláusula ORDER BY para devolver las filas en el orden que desee

Quitar de SELECT enumere las expresiones que devuelven tot_bookings y tot_billable .

EDITAR

Creo que me perdí los criterios de fecha. Podemos convertir las uniones externas en uniones internas y reemplazar la UNIÓN CRUZADA con una UNIÓN IZQUIERDA. Tenemos potencial para devolver valores NULL para cnt_bookings y cnt_billable , podemos envolverlos en la función IFNULL() o COALESCE() para reemplazar NULL con cero.

 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