sql >> Databáze >  >> RDS >> Mysql

Vraťte hodnocení z více tabulek pomocí mySQL

Navrhuji, abychom dotaz sestavili postupně, krok za krokem. Ověřte, zda jsou výsledky dotazu takové, jaké očekáváme v každém kroku. Když něco "nefunguje", zálohujte krok.

Chceme vrátit tři řádky, jeden pro každý řádek v ___Segmentations , pro konkrétní hotelid

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

Přidejte vnější spojení 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

Přidejte vnější spojení 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

Pokud jsou to řádky, které nás zajímají, můžeme pracovat na agregaci.

 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

Nyní k získání agregace s "celkem".

Přístup, který bych zvolil, by byl vytvoření „kopií“ řádků pomocí operace CROSS JOIN. Můžeme provést spojení s řádky vrácenými úplně prvním dotazem, který jsme napsali, označovaným jako vložený pohled. (Alias ​​jako q níže.)

Pokud máme úplnou sadu řádků, opakujících se pro každý seg_id/seg_text (první dotaz, který jsme napsali), můžeme použít podmíněnou agregaci.

Poslední dotaz, který jsme napsali (těsně výše), je vložený pohled v dotazu níže s aliasem c .

SOUČET cnt_bookings ze všech řádků je součet.

Pro jednotlivé počty můžeme zahrnout pouze řádky, které mají odpovídající seg_id , celkem tato podmnožina.

 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

V SELECT seznamu, můžeme provést rozdělení, abychom získali procento:cnt_bookings * 100.0 / tot_bookings

např.

 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

Upravte klauzuli ORDER BY tak, aby vracela řádky v požadovaném pořadí

Odebrat z SELECT seznam výrazů, které vracejí tot_bookings a tot_billable .

UPRAVIT

Myslím, že jsem přehlédl datumové kritérium. Z vnějšího spojení můžeme udělat vnitřní spojení a CROSS JOIN nahradit LEFT JOIN. Máme potenciál vrátit hodnoty NULL pro cnt_bookings a cnt_billable , můžeme je zabalit do funkce IFNULL() nebo COALESCE() a nahradit tak NULL nulou.

 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 Server 2008 Časová razítka vložení a aktualizace řádků

  2. Cizí klíče a NULL v mySQL

  3. Chyba nesprávné hodnoty INTEGER zkrácena MYSQL

  4. Oracle - doslovný neodpovídá chyba formátovacího řetězce