MySQL GROUP BY和限制连接

时间:2012-01-06 11:48:54

标签: mysql group-by

我正在尝试对以下三个表执行查询:

订单

id

created_at

id

order_id

initial_value

current_value

vouchers_used_in_orders

voucher_id

order_id

amount_used

value_before

value_after

在订单中购买凭证,然后将此订单的ID保存到凭证中,以记录购买时的凭证。

以后,凭证可用于以其他订单购买商品。每张优惠券都有现金价值,因此可以在多个订单中使用,直到其价值完全用完为止。

我正在尝试提出一个查询,根据购买的年份和月份对凭证购买进行分组,并显示有多少人已被清除,以及在首次使用之前已经过了多少时间,最长可达12个月。< / p>

这是我到目前为止所做的:

SELECT
YEAR(o.created_at) AS year_purchased,
MONTH(o.created_at) AS month_purchased,
SUM(IF(v.current_value = v.initial_value, 1, 1)) AS number_sold,
SUM(IF(v.current_value = v.initial_value, 0, 1)) AS number_used,
SUM(IF(v.current_value = v.initial_value, 1, 0)) AS number_not_used,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at) AND MONTH(vuo.created_at) = (MONTH(o.created_at)), 1, 0)) AS number_used_month_0,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 1 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 1 MONTH)), 1, 0)) AS number_used_month_1,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 2 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 2 MONTH)), 1, 0)) AS number_used_month_2,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 3 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 3 MONTH)), 1, 0)) AS number_used_month_3,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 4 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 4 MONTH)), 1, 0)) AS number_used_month_4,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 5 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 5 MONTH)), 1, 0)) AS number_used_month_5,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 6 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 6 MONTH)), 1, 0)) AS number_used_month_6,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 7 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 7 MONTH)), 1, 0)) AS number_used_month_7,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 8 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 8 MONTH)), 1, 0)) AS number_used_month_8,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 9 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 9 MONTH)), 1, 0)) AS number_used_month_9,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 10 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 10 MONTH)), 1, 0)) AS number_used_month_10,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 11 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 11 MONTH)), 1, 0)) AS number_used_month_11,
SUM(IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 12 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 12 MONTH)), 1, 0)) AS number_used_month_12
FROM vouchers v
INNER JOIN orders o
ON o.id = v.order_id
LEFT OUTER JOIN vouchers_used_in_orders vu
ON vu.voucher_id = v.id
LEFT OUTER JOIN orders vuo
ON vuo.id = vu.id
GROUP BY
YEAR(o.created_at), MONTH(o.created_at)
ORDER BY
YEAR(o.created_at), MONTH(o.created_at)

如果购买的优惠券未被使用,则会对其进行正确计数,但一旦开始使用,外部联接会导致优惠券被多次计算。此外,当我只想第一次使用时,它会计算凭证的每次使用。

有人可以帮忙吗?

任何建议表示赞赏。

感谢。

3 个答案:

答案 0 :(得分:1)

您可以在优惠券ID上使用COUNT DISTINCT吗? (您需要将if语句中的false条件设置为null,因此它们不是计数器)

SELECT
YEAR(o.created_at) AS year_purchased,
MONTH(o.created_at) AS month_purchased,
SUM(IF(v.current_value = v.initial_value, 1, 1)) AS number_sold,
SUM(IF(v.current_value = v.initial_value, 0, 1)) AS number_used,
SUM(IF(v.current_value = v.initial_value, 1, 0)) AS number_not_used,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at) AND MONTH(vuo.created_at) = (MONTH(o.created_at)), vuo.voucher_id, null)) AS number_used_month_0,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 1 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 1 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_1,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 2 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 2 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_2,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 3 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 3 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_3,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 4 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 4 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_4,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 5 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 5 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_5,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 6 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 6 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_6,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 7 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 7 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_7,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 8 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 8 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_8,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 9 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 9 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_9,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 10 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 10 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_10,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 11 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 11 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_11,
COUNT(DISTINCT IF(YEAR(vuo.created_at) = YEAR(o.created_at + INTERVAL 12 MONTH) AND MONTH(vuo.created_at) = (MONTH(o.created_at + INTERVAL 12 MONTH)), vuo.voucher_id, null)) )) AS number_used_month_12
FROM vouchers v
INNER JOIN orders o
ON o.id = v.order_id
LEFT OUTER JOIN vouchers_used_in_orders vu
ON vu.voucher_id = v.id
LEFT OUTER JOIN orders vuo
ON vuo.id = vu.id
GROUP BY
YEAR(o.created_at), MONTH(o.created_at)
ORDER BY
YEAR(o.created_at), MONTH(o.created_at)

答案 1 :(得分:1)

这应该会给你几乎所有你想要的东西。除了每月的计数外,我还添加了每个月使用的总金额。我首先开始查询以获得凭证,其初始日期以及每个您关注的12个月。我在每个“每个凭证”的基础上预先计算了一个月,因此它可以帮助简化聚合的SUM(IF())结构。为了检测单张凭证,在我的预查询(V1)中, 我保留了与购买优惠券相关的原始订单。因此,通过使用它,我可以执行相同订单ID的SUM(IF())。如果是那个,它会在VouchersUsed中计算。左边连接到凭证使用表,如果返回为空,则没有使用它的实例,并且将是NULL值。

您会注意到,我还采取了预防措施,以防止通过LEFT JOIN包含原始订单,明确阻止用于购买凭证的订单ID被计算并错误地丢弃计数。

  LEFT JOIN Vouchers_Used_In_Orders VUIO
     ON V1.ID = VUIO.Voucher_ID
     AND NOT V1.OriginalOrderID = VUIO.Order_ID

我唯一无法测试的元素,现在没有MySQL

SUM(IF(VUIO.Voucher_ID = null,1,0))as VouchersNotUsed,

我认为它会去,但可能需要将其更改为SUM(IF(VUIO.Voucher_ID为null,1,0))。希望这会帮助你。

SELECT 
      YEAR( v1.VoucherStarted ) as VoucherYear,
      MONTH( v1.VoucherStarted ) as VoucherMonth,
      COUNT( distinct( v1.ID )) as UniqueVouchers,
      SUM( IF( v1.OriginalOrderID = O.ID, 1, 0 )) as VouchersUsed,
      SUM( IF( VUIO.Voucher_ID = null, 1, 0 )) as VouchersNotUsed,
      SUM( IF( v1.OriginalOrderID = O.ID, 0, 1 )) as TimesVouchersUsed,

      SUM( IF( O.Created_At >= v1.VoucherStarted AND O.Created_At < v1.Plus1Month ), 1, 0 ) as Month1Cnt,
      SUM( IF( O.Created_At >= v1.VoucherStarted AND O.Created_At < v1.Plus1Month ), VUIO.Amount_Used , 0 ) as Month1Amt,

      SUM( IF( O.Created_At >= v1.Plus1Month AND O.Created_At < v1.Plus2Month ), 1, 0 ) as Month2Cnt,
      SUM( IF( O.Created_At >= v1.Plus1Month AND O.Created_At < v1.Plus2Month ), VUIO.Amount_Used , 0 ) as Month2Amt,

      SUM( IF( O.Created_At >= v1.Plus2Month AND O.Created_At < v1.Plus3Month ), 1, 0 ) as Month3Cnt,
      SUM( IF( O.Created_At >= v1.Plus2Month AND O.Created_At < v1.Plus3Month ), VUIO.Amount_Used , 0 ) as Month3Amt,

      SUM( IF( O.Created_At >= v1.Plus3Month AND O.Created_At < v1.Plus4Month ), 1, 0 ) as Month3Cnt,
      SUM( IF( O.Created_At >= v1.Plus3Month AND O.Created_At < v1.Plus4Month ), VUIO.Amount_Used , 0 ) as Month3Amt,

      SUM( IF( O.Created_At >= v1.Plus4Month AND O.Created_At < v1.Plus5Month ), 1, 0 ) as Month4Cnt,
      SUM( IF( O.Created_At >= v1.Plus4Month AND O.Created_At < v1.Plus5Month ), VUIO.Amount_Used , 0 ) as Month4Amt,

      SUM( IF( O.Created_At >= v1.Plus5Month AND O.Created_At < v1.Plus6Month ), 1, 0 ) as Month5Cnt,
      SUM( IF( O.Created_At >= v1.Plus5Month AND O.Created_At < v1.Plus6Month ), VUIO.Amount_Used , 0 ) as Month5Amt,

      SUM( IF( O.Created_At >= v1.Plus6Month AND O.Created_At < v1.Plus7Month ), 1, 0 ) as Month6Cnt,
      SUM( IF( O.Created_At >= v1.Plus6Month AND O.Created_At < v1.Plus7Month ), VUIO.Amount_Used , 0 ) as Month6Amt,

      SUM( IF( O.Created_At >= v1.Plus7Month AND O.Created_At < v1.Plus8Month ), 1, 0 ) as Month7Cnt,
      SUM( IF( O.Created_At >= v1.Plus7Month AND O.Created_At < v1.Plus8Month ), VUIO.Amount_Used , 0 ) as Month7Amt,

      SUM( IF( O.Created_At >= v1.Plus8Month AND O.Created_At < v1.Plus9Month ), 1, 0 ) as Month8Cnt,
      SUM( IF( O.Created_At >= v1.Plus8Month AND O.Created_At < v1.Plus9Month ), VUIO.Amount_Used , 0 ) as Month8Amt,

      SUM( IF( O.Created_At >= v1.Plus9Month AND O.Created_At < v1.Plus10Month ), 1, 0 ) as Month9Cnt,
      SUM( IF( O.Created_At >= v1.Plus9Month AND O.Created_At < v1.Plus10Month ), VUIO.Amount_Used , 0 ) as Month9Amt,

      SUM( IF( O.Created_At >= v1.Plus10Month AND O.Created_At < v1.Plus11Month ), 1, 0 ) as Month10Cnt,
      SUM( IF( O.Created_At >= v1.Plus10Month AND O.Created_At < v1.Plus11Month ), VUIO.Amount_Used , 0 ) as Month10Amt,

      SUM( IF( O.Created_At >= v1.Plus11Month AND O.Created_At < v1.Plus12Month ), 1, 0 ) as Month11Cnt,
      SUM( IF( O.Created_At >= v1.Plus11Month AND O.Created_At < v1.Plus12Month ), VUIO.Amount_Used , 0 ) as Month11Amt,

      SUM( IF( O.Created_At >= v1.Plus12Month ), 1, 0 ) as Month12Cnt,
      SUM( IF( O.Created_At >= v1.Plus12Month ), VUIO.Amount_Used , 0 ) as Month12Amt

   from 
      ( SELECT
              v.id,
              v.initial_value,
              v.order_id as OriginalOrderID
              vo.created_at as VoucherStarted,
              date_add( vo.created_at, INTERVAL 1 MONTH ) as Plus1Month,
              date_add( vo.created_at, INTERVAL 2 MONTH ) as Plus2Month,
              date_add( vo.created_at, INTERVAL 3 MONTH ) as Plus3Month,
              date_add( vo.created_at, INTERVAL 4 MONTH ) as Plus4Month,
              date_add( vo.created_at, INTERVAL 5 MONTH ) as Plus5Month,
              date_add( vo.created_at, INTERVAL 6 MONTH ) as Plus6Month,
              date_add( vo.created_at, INTERVAL 7 MONTH ) as Plus7Month,
              date_add( vo.created_at, INTERVAL 8 MONTH ) as Plus8Month,
              date_add( vo.created_at, INTERVAL 9 MONTH ) as Plus9Month,
              date_add( vo.created_at, INTERVAL 10 MONTH ) as Plus10Month,
              date_add( vo.created_at, INTERVAL 12 MONTH ) as Plus11Month,
              date_add( vo.created_at, INTERVAL 12 MONTH ) as Plus12Month
           from
              vouchers v
                 join orders vo
                    ON v.order_id = vo.order_id ) V1

      LEFT JOIN Vouchers_Used_In_Orders VUIO
         ON V1.ID = VUIO.Voucher_ID
         AND NOT V1.OriginalOrderID = VUIO.Order_ID

         LEFT JOIN Orders O
            ON VUIO.Order_ID = O.ID
            AND O.Created_At between V1.VoucherStarted and V1.Plus12Month

   GROUP BY
      YEAR( v1.VoucherStarted ),
      MONTH( v1.VoucherStarted )

答案 2 :(得分:0)

我将'代金券'和'订单'合并到:

 voucher_orders:
     ID
     purchase_date
     value

然后有'voucher_use':

     VoucherID
     Amount
     use_date

获取统计数据:

 select voucher_order.ID as v0, voucher_order.purchase_date, v2.ud left join
 (  select min( use_date ) ud from voucher_use as v1 where v1.VoucherID = v0.VoucherID limit 1 ) as v2 on v0.VoucherID = v2.VoucherID group by v0.purchase_date order by v0.purchase_date;

在代码中完成所有'剩余价值'的东西。此信息无需存储。