MySQL高级查询Brainteaser

时间:2009-04-29 18:18:25

标签: mysql sql

我被要求创建一份财务报告,需要在几个“推荐人”的两个日期之间提供总佣金率。这很容易。

困难的部分是佣金率不仅取决于推荐人,而且关于推荐类型以及对该类推荐人数的影响已由特定推荐人制作。

跟踪推荐人数需要考虑所有推荐,而不是指定日期范围内的推荐 - 换句话说,每个推荐人的佣金率都在滑动范围内,随着推荐总数的增加而变化。幸运的是,每种推荐类型最多只有3个佣金级别。

引荐都存储在同一个表中,每个引用1行,其中一个字段表示引用者和引用类型。举例说明:

ID   Type    Referrer    Date
1    A       X           01/12/08
2    A       X           15/01/09
3    A       X           23/02/09
4    B       X           01/12/08
5    B       X           15/01/09
6    A       Y           01/12/08
7    A       Y           15/01/09
8    B       Y           15/01/09
9    B       Y           23/02/09

佣金率不会存储在推荐表中 - 实际上可能会更改 - 而是存储在推荐人表中,如下所示:

Referrer    Comm_A1    Comm_A2    Comm_A3    Comm_B1    Comm_B2    Comm_B3
X           30         20         10         55         45         35
Y           45         35         25         60         40         30

以上述推荐表为例,假设佣金率水平在推荐号1和2之后增加(然后保持不变),运行2008年12月至2009年2月的佣金报告将返回以下内容:

[编辑] - 为了澄清上述情况,佣金率对于每种类型和每个推荐人都有三个级别,第一个推荐佣金的初始费率为Comm_A1,第二个推荐佣金为Comm_A2,所有后续推荐的Comm_A3。

Referrer    Type_A_Comm    Type_A_Ref    Type_B_Comm    Type_B_Ref
X           60             3             100            2
Y           80             2             100            2

2009年2月运行佣金报告将返回:

Referrer    Type_A_Comm    Type_A_Ref    Type_B_Comm    Type_B_Ref
X           10             1             0              0
Y           0              0             40             1

修改上述结果已根据我的原始问题进行了调整,就列/行分组而言。

我很确定任何解决方案都会涉及一个子查询(可能是针对每个推荐类型)以及某种聚合/总和如果 - 但我正在努力想出一个有效的查询。

[编辑] 我不确定是否要写出我的要求的等式,但我会尝试列出我看到的步骤:

确定每种类型和每个推荐人的先前推荐数量 - 也就是说,无论日期范围如何。

根据之前推介的数量,选择适当的佣金等级 - 0先前=等级1,1先前=等级2,2或更高之前=等级3

(注意:推荐人没有以前的推荐,但是,例如,3个新的推荐人,预计佣金为1 x 1级,1 x 2级,1 x 3级=总佣金)

根据日期范围过滤结果 - 以便确定一段活动的应付佣金。

返回带有referrer列的数据,以及一个包含每个推介类型总佣金的列(理想情况下,还有一个每个推荐类型都有一个计数的列)。

这有助于澄清我的要求吗?

3 个答案:

答案 0 :(得分:4)

假设您有一个名为type的表,列出了您的特定推荐类型,这应该有效(如果没有,您可以替换另一个子选项,以便为此目的从引用中获取不同的类型)。

select
    r.referrer,
    t.type,
    (case 
        when isnull(ref_prior.referrals, 0) < @max1 then 
            (case 
                when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) < @max1 then isnull(ref_period.referrals, 0) 
                else @max1 - isnull(ref_prior.referrals, 0) 
            end) 
        else 0 
    end) * (case t.type when 'A' then r.Comm_A1 when 'B' then r.Comm_B1 else null end) +
    (case when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) > @max1 then
        (case 
            when isnull(ref_prior.referrals, 0) < @max2 then 
                (case 
                    when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) < @max2 then isnull(ref_period.referrals, 0) 
                    else @max2 - isnull(ref_prior.referrals, 0) 
                end) 
            else 0 
        end) -
        (case 
            when isnull(ref_prior.referrals, 0) < @max1 then 
                (case 
                    when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) < @max1 then isnull(ref_period.referrals, 0) 
                    else @max1 - isnull(ref_prior.referrals, 0) 
                end) 
            else 0 
        end)
    else 0 end) * (case t.type when 'A' then r.Comm_A2 when 'B' then r.Comm_B2 else null end) +
    (case when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) > @max2 then
        (isnull(ref_period.referrals, 0)) -
            (
                (case when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) > @max1 then
                    (case 
                        when isnull(ref_prior.referrals, 0) < @max2 then 
                            (case 
                                when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) < @max2 then isnull(ref_period.referrals, 0) 
                                else @max2 - isnull(ref_prior.referrals, 0) 
                            end) 
                        else 0 
                    end) -
                    (case 
                        when isnull(ref_prior.referrals, 0) < @max1 then 
                            (case 
                                when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) < @max1 then isnull(ref_period.referrals, 0) 
                                else @max1 - isnull(ref_prior.referrals, 0) 
                            end) 
                        else 0 
                    end)
                else 0 end) +
                (case 
                    when isnull(ref_prior.referrals, 0) < @max1 then 
                        (case 
                            when isnull(ref_prior.referrals, 0) + isnull(ref_period.referrals, 0) < @max1 then isnull(ref_period.referrals, 0) 
                            else @max1 - isnull(ref_prior.referrals, 0) 
                        end) 
                    else 0 
                end)
            )                   
    else 0 end) * (case t.type when 'A' then r.Comm_A3 when 'B' then r.Comm_B3 else null end) as Total_Commission

from referrer r

join type t on 1 = 1 --intentional cartesian product
left join (select referrer, type, count(1) as referrals from referral where date < @start_date group by referrer, type) ref_prior on ref_prior.referrer = r.referrer and ref_prior.type = t.type
left join (select referrer, type, count(1) as referrals from referral where date between @start_date and @end_date group by referrer, type) ref_period on ref_period.referrer = r.referrer and ref_period.type = t.type

这假设您有一个@start_date@end_date变量,您显然必须提供case语句中缺少的逻辑,以根据类型和数量正确选择费率来自ref_total的推荐。

修改

在回顾完问题之后,我看到了关于滑动比例的评论。这大大增加了查询的复杂性,但它仍然可行。修改后的查询现在还取决于两个变量@max1@max2的存在,表示可归入类别“1”和类别“2”的最大销售数量(出于测试目的,我使用过分别为1和2,这些产生了预期的结果)。

答案 1 :(得分:2)

亚当的回答远比我想要的要彻底,但我认为尝试将其作为单一查询来写可能不是正确的方法。

您是否考虑过创建一个存储过程,然后逐步创建并填充临时表。

临时表将具有您正在寻找的结果集的形状。初始插入创建基本数据集(基本上是您希望返回的行数,包含键标识符,然后是您希望返回的任何其他内容,可以轻松地将其组合为同一查询的一部分)。

然后,您可以对临时表进行一系列更新,以组合更复杂数据的每个部分。

最后选择全部然后放下临时表。

这样做的好处是它可以让你在脑海中分解并稍微组装一下,这样你就可以更容易地找到出错的地方。这也意味着可以在几个阶段组装更复杂的位。

此外,如果一些可怜的草皮出现并且之后必须调试整个事情,那么他将更容易追踪发生在哪里的事情。

答案 2 :(得分:0)

编辑:这个答案没有考虑到以下要求,但似乎有一堆新的解释,所以我想我会把它留在这里......

  

跟踪引荐数量需要考虑所有引荐,而不是指定日期范围内的引荐

好的,假设报告期是每月一次,并使用CASE实际上IF可以区分两个有效费率(对于count = 1和count&gt; 1),那么:

select
  ref.month, 
  ref.referrer, 
  ref.type,
  ( ref.count * 
      case ref.type
        when 'A' then
          case ref.count
            -- not useful: when 0 then com.Comm_A1
            when 1 then com.Comm_A2
            else com.Comm_A3
          end case
        when 'B' then
          case ref.count
            -- not useful: when 0 then com.Comm_B1
            when 1 then com.Comm_B2
            else com.Comm_B3
          end case
      end case 
  ) as total_commission
from
  ( select
      date_format(date, '%Y-%m') as month,
      referrer,
      type,
      count(*) as count
    from referrals
    group by month, referrer, type
  ) as ref,
join commissions com on com.referrer = ref.referrer

(我猜上面没有选择'ref'和'count'等名称。)