MySQL Advanced Query Brainteaser - PullRequest
       4

MySQL Advanced Query Brainteaser

3 голосов
/ 29 апреля 2009

Меня попросили создать финансовый отчет, в котором необходимо указать общую ставку комиссии между двумя датами для нескольких «рефереров». Это легкая часть.

Сложность в том, что размер комиссии варьируется в зависимости не только от реферала, но и также от типа реферала , а также от количества рефералов такого типа, которые были сделан данным реферером.

Отслеживание количества рефералов должно учитывать ВСЕ рефералы, а не те, которые находятся в данном диапазоне дат - иными словами, размер комиссии для каждого реферала находится на скользящей шкале и изменяется по мере увеличения их общего количества рефералов. К счастью, для каждого типа реферала существует максимум 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 года по февраль 2009 года будет возвращено следующее:

[Редактировать] - чтобы пояснить вышесказанное, ставка комиссии имеет три уровня для каждого типа и каждого реферера, с начальной ставкой 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 г. вернет:

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

Редактировать вышеупомянутые результаты были скорректированы с моего исходного вопроса, с точки зрения группировки столбцов / строк.

Я вполне уверен, что любое решение будет включать в себя подзапрос (возможно, для каждого типа рефералов), а также какой-то агрегат / Sum If - но я изо всех сил пытаюсь придумать рабочий запрос.

[Редактировать] Я не уверен в написании уравнения моих требований, но я постараюсь перечислить шаги, как я их вижу:

Определите количество предыдущих рефералов для каждого типа и каждого реферала, то есть независимо от диапазона дат.

На основе количества предыдущих рефералов выберите соответствующий уровень комиссии - 0 предыдущий = уровень 1, 1 предыдущий = уровень 2, 2 или более предыдущий = уровень 3

(Примечание: реферер, у которого нет предыдущих рефералов, но, скажем, 3 новых реферала, ожидает комиссию 1 x уровень 1, 1 x уровень 2, 1 x уровень 3 = общая комиссия)

Фильтрация результатов по диапазону дат - так, чтобы можно было определить комиссионные, выплачиваемые за период деятельности.

Возвращает данные со столбцом для реферера и столбцом с общей комиссией для каждого типа рефералов (и в идеале также столбец с количеством для каждого типа рефералов).

Это помогает уточнить мои требования?

Ответы [ 3 ]

4 голосов
/ 29 апреля 2009

Предполагая, что у вас есть таблица с именем 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 соответственно и это дало ожидаемые результаты).

2 голосов
/ 30 апреля 2009

Ответ Адама гораздо более тщательный, чем я собираюсь быть, но я думаю, что попытка написать это как один запрос может быть неправильным подходом.

Задумывались ли вы о создании хранимой процедуры, которая шаг за шагом создает и затем заполняет временную таблицу.

Временная таблица будет иметь форму набора результатов, который вы ищете. Первоначальная вставка создает ваш базовый набор данных (по сути, количество строк, которые вы хотите вернуть с ключевыми идентификаторами, а затем все, что вы хотите вернуть, которые можно легко собрать как часть того же запроса).

Затем у вас будет ряд обновлений для временной таблицы, собирающих каждый раздел из более сложных данных.

Наконец, верните все обратно и удалите временную таблицу.

Преимущества этого в том, что он позволяет вам разбить его на части в уме и собрать его немного за раз, что позволяет вам легче находить ошибки. Это также означает, что более сложные биты могут быть собраны в несколько этапов.

Кроме того, если придет какой-то бедняга и потом все отладит, ему будет гораздо легче проследить, что и где происходит.

0 голосов
/ 30 апреля 2009

РЕДАКТИРОВАТЬ: этот ответ не принимает во внимание следующее требование, но, кажется, есть куча новых объяснений, поэтому я думаю, я оставлю это здесь, как есть ...

Отслеживание количества рефералов должно учитывать ВСЕ рефералы, а не те, которые находятся в данном диапазоне дат

Хорошо, если предположить, что отчетный период является ежемесячным, и использовать CASE, где на самом деле IF может также различать две действительные ставки (для count = 1 и count> 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', выбраны не слишком хорошо).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...