Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Geben Sie mit mySQL ein Ranking aus mehreren Tabellen zurück

Ich schlage vor, dass wir die Abfrage Schritt für Schritt inkrementell erstellen. Überprüfen Sie bei jedem Schritt, ob die Abfrageergebnisse unseren Erwartungen entsprechen. Wenn etwas "nicht funktioniert", sichern Sie einen Schritt.

Wir möchten drei Zeilen zurückgeben, eine für jede Zeile in ___Segmentations , für eine bestimmte hotelid

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

Fügen Sie den äußeren Join zu __Bookings hinzu

 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

Fügen Sie den äußeren Join zu ___BillableDatas hinzu

 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

Wenn das die Zeilen sind, an denen wir interessiert sind, können wir an der Aggregation arbeiten.

 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

Jetzt erhalten Sie die Aggregation mit der "Gesamtsumme".

Der Ansatz, den ich wählen würde, wäre, "Kopien" der Zeilen mit einer CROSS JOIN-Operation zu erstellen. Wir können die Zeilen verknüpfen, die von der allerersten von uns geschriebenen Abfrage zurückgegeben wurden, die als Inline-Ansicht referenziert wird. (Alasname q unten.)

Wenn wir einen vollständigen Satz von Zeilen haben, wird dies für jeden seg_id/seg_text wiederholt (diese erste Abfrage, die wir geschrieben haben), können wir die bedingte Aggregation verwenden.

Die letzte Abfrage, die wir geschrieben haben (direkt oben), ist eine Inline-Ansicht in der Abfrage unten, mit dem Alias ​​c .

SUMME von cnt_bookings aus allen Zeilen ist die Summe.

Für die einzelnen Zählungen können wir nur die Zeilen einbeziehen, die eine übereinstimmende seg_id haben , insgesamt dieser Teilmenge.

 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

Im SELECT list, können wir dividieren, um den Prozentsatz zu erhalten:cnt_bookings * 100.0 / tot_bookings

z. B.

 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

Ändern Sie die ORDER BY-Klausel, um die Zeilen in der gewünschten Reihenfolge zurückzugeben

Aus SELECT entfernen listen Sie die Ausdrücke auf, die tot_bookings zurückgeben und tot_billable .

BEARBEITEN

Ich glaube, ich habe die Datumskriterien übersehen. Wir können die äußeren Joins in innere Joins umwandeln und den CROSS JOIN durch einen LEFT JOIN ersetzen. Wir haben die Möglichkeit, NULL-Werte für cnt_bookings zurückzugeben und cnt_billable , können wir diese in die Funktion IFNULL() oder COALESCE() einschließen, um NULL durch Null zu ersetzen.

 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