这是我的表结构:
___郎:
|--------|------------|
| LAN_Id | LAN_En |
|--------|------------|
| DI | Direct |
| WE | Web |
| OT | Other |
|--------|------------|
___分割
|--------|------------|
| SEG_Id | SEG_Code |
|--------|------------|
| 1 | DI |
| 2 | WE |
| 3 | OT |
|--------|------------|
___预订:
|--------|------------------|
| BOO_Id | BOO_Segmentation |
|--------|------------------|
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
|--------|------------------|
___ BillableDatas:
|--------|---------------|------------|------------|
| BIL_Id | BIL_BookingId | BIL_Date | BIL_Item |
|--------|---------------|------------|------------|
| 1 | 1 | 2017-02-21 | Night |
| 2 | 1 | 2017-02-22 | Night |
| 3 | 1 | 2017-02-23 | Night |
| 4 | 1 | 2017-02-24 | Night |
| 5 | 2 | 2017-02-25 | Night |
| 6 | 2 | 2017-02-26 | Night |
| 7 | 3 | 2017-02-28 | Night |
| 8 | 3 | 2017-03-01 | Night |
| 9 | 3 | 2017-03-02 | Night |
| 10 | 3 | 2017-03-03 | Night |
|--------|---------------|------------|------------|
我想知道一系列日期中最受欢迎的细分。
对于以下日期范围,所需的结果应为此结果:
表格2017-02-01至2017-02-28包含
|------------|------------|------------|--------------|------------|
| ROO_Name | Night_Nb | Percentage | Booking_Nb | Percentage |
|------------|------------|------------|--------------|------------|
| Direct | 6 | 85.71 | 2 | 66.66 |
| Website | 1 | 14.28 | 1 | 33.33 |
| Other | 0 | 0 | 0 | 0 |
|------------|------------|------------|--------------|------------|
我已经尝试过:
SELECT r.SEG_Id
, Sum(CASE WHEN BOO_Id IS NULL THEN 0 ELSE 1 END) Night_Nb
, Concat(
Format(
Sum(CASE WHEN BOO_Id IS NULL THEN 0 ELSE 1 END)
/ TotalBookings
* 100
, 0) ) AS PercentageTotal
FROM ( ___Segmentations r LEFT JOIN ___Bookings b ON r.SEG_Id = b.BOO_Segmentation
) INNER JOIN (SELECT BOO_HotelId
, Count(*) AS TotalBookings
FROM ___Bookings
GROUP BY BOO_HotelId
) AS TotalHotelBookings
ON r.SEG_HotelId = TotalHotelBookings.BOO_HotelId
WHERE r.SEG_HotelId = :hotel_id
GROUP BY r.SEG_Id
ORDER BY NumBookings DESC
但它实际上并没有起作用。
有人可以帮我这个吗?
你可以使用SQL Fiddle: http://sqlfiddle.com/#!9/1aa10a
答案 0 :(得分:0)
不确定100 {%hotelId
列如何在这里发挥作用,你没有在问题中描述它,但试试这个:
SELECT aaa.SEG_Text, aaa.Night_NB, aaa.Night_NB / totals.total_nights * 100, aaa.Booking_Nb , aaa.Booking_Nb / totals.total_bookings * 100 FROM (
SELECT s.SEG_Text, COUNT(DISTINCT d.BIL_Id) AS `Night_Nb`, COUNT(DISTINCT b.BOO_Id) AS `Booking_Nb` FROM ___Segmentations s
LEFT JOIN ___Bookings b ON s.SEG_Id = b.BOO_Segmentation
LEFT JOIN ___BillableDatas d ON d.BIL_BookingId = b.BOO_Id AND d.BIL_Date BETWEEN '2017-02-01' AND '2017-02-28'
GROUP BY s.SEG_Id ) AS `aaa`
, ( SELECT COUNT(*) AS `total_nights`, COUNT(DISTINCT BIL_BookingId) `total_bookings` FROM ___BillableDatas WHERE BIL_Date BETWEEN '2017-02-01' AND '2017-02-28') AS totals
它基本上和你做的一样,但使用SELECT(DISTINCT ...)
,因此更容易理解,调试,我认为也会更快。
对我来说,它会返回正确的结果。
答案 1 :(得分:0)
这里的挑战似乎是避免做两次相同的查询(重复日期条件)以计算两个百分比。
您可以使用with rollup
修饰符生成计算这些百分比所需的总计数。然后,您可以在变量中捕获这些总计,并在包装查询中将它们用作除数。最后,外部查询的where
子句将消除rollup
记录,因为它已达到其目的:
select seg_text
, night_nb
, 100*night_nb/@sum_night_nb as night_pct
, booking_nb
, 100*booking_nb/@sum_booking_nb as booking_pct
from (
select seg_text
, @sum_night_nb := count(bil_id) night_nb
, @sum_booking_nb := count(distinct bil_bookingid) booking_nb
from ___segmentations seg
left join (___bookings boo
inner join ___billabledatas bil
on bil_bookingid = boo_id
and bil_hotelid = boo_hotelid)
on seg_id = boo_segmentation
and seg_hotelid = boo_hotelid
and bil_date between '2017-02-01' and '2017-02-28'
where seg_hotelid = 'AAA00'
group by seg_text with rollup
) base
where seg_text is not null
order by night_nb desc
答案 2 :(得分:0)
我建议我们逐步逐步构建查询。验证查询结果是否与我们在每个步骤中所期望的一样。当某些内容“无效”时,请备份一个步骤。
我们希望为___Segmentations
中的每一行返回三行,一行为hotelid
SELECT r.seg_id
, r.seg_text
FROM ___Segmentations r
WHERE r.seg_hotelid = :hotel_id
ORDER BY r.seg_id
将外部联接添加到__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
将外部联接添加到___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
如果这是我们感兴趣的行,我们可以进行聚合。
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
现在使用“total”进行聚合。
我将采用的方法是使用CROSS JOIN操作制作行的“副本”。我们可以连接到我们编写的第一个查询返回的行,引用为内联视图。 (别名为q
以下。)
如果我们有一组完整的行,对每个seg_id/seg_text
(我们写的第一个查询)重复,我们可以使用条件聚合。
我们编写的最后一个查询(上图)是下面查询中的内联视图,别名为c
。
所有行的cnt_bookings
的和是总数。
对于个别计数,我们只能包含匹配seg_id
的行,即该子集的总数。
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
在SELECT
列表中,我们可以进行除法以获得百分比:cnt_bookings * 100.0 / tot_bookings
e.g。
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
修改ORDER BY子句以按您想要的顺序返回行
从SELECT
列表中删除返回tot_bookings
和tot_billable
的表达式。
修改强>
我想我错过了日期标准。我们可以将外连接设置为内连接,并用LEFT JOIN替换CROSS JOIN。我们有可能返回cnt_bookings
和cnt_billable
的NULL值,我们可以将它们包含在IFNULL()或COALESCE()函数中,用零替换NULL。
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