sql >> Databasteknik >  >> RDS >> Mysql

Returnera en rankning från flera tabeller med mySQL

Jag föreslår att vi bygger frågan stegvis, steg för steg. Verifiera att frågeresultaten är som vi förväntar oss vid varje steg. När något "inte fungerar", säkerhetskopiera ett steg.

Vi vill returnera tre rader, en för varje rad i ___Segmentations , för ett specifikt hotelid

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

Lägg till den yttre kopplingen till __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

Lägg till den yttre kopplingen till ___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

Om det är de rader vi är intresserade av kan vi arbeta med aggregering.

 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

Nu för att få aggregeringen med "total".

Tillvägagångssättet jag skulle ta skulle vara att göra "kopior" av raderna med en CROSS JOIN-operation. Vi kan göra kopplingen till raderna som returneras av den allra första frågan vi skrev, refererad till som en inlinevy. (Aliaserad som q nedan.)

Om vi ​​har en komplett uppsättning rader, upprepas för varje seg_id/seg_text (den första frågan vi skrev), kan vi använda villkorlig aggregering.

Den sista frågan vi skrev (strax ovan) är en inlinevy i frågan nedan, alias som c .

SUMMA för cnt_bookings från alla rader är summan.

För de individuella räkningarna kan vi bara inkludera de rader som har ett matchande seg_id , totalt den delmängden.

 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

I SELECT listan kan vi göra divisionen för att få procenttalet:cnt_bookings * 100.0 / tot_bookings

t.ex.

 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

Ändra ORDER BY-satsen för att returnera raderna i den ordning du vill ha

Ta bort från SELECT lista uttrycken som returnerar tot_bookings och tot_billable .

REDIGERA

Jag tror att jag missade datumkriteriet. Vi kan göra de yttre fogarna till inre skarvar och ersätta CROSS JOIN med en LEFT JOIN. Vi har potential att returnera NULL-värden för cnt_bookings och cnt_billable , kan vi slå in dem i IFNULL()- eller COALESCE()-funktionen för att ersätta NULL med noll.

 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


  1. SQL Välj de vanligaste värdena

  2. Introduktion till temporära tabeller i SQL Server

  3. Skapa funktioner i phpMyAdmin - Fel:åtkomst nekad du behöver superprivilegiet för denna operation

  4. Fjärråtkomst till MySQL-server via SSH-tunnel