Интересный, возможно, сложный SQL-запрос для определения количества последовательных месяцев - PullRequest
2 голосов
/ 02 ноября 2011

Прежде всего, спасибо всем, кто помогает мне ответить на этот вопрос! Я гуглил и пытался узнать как можно больше о рекурсивных CTE и других расширенных функциях MS SQL 05-08, но я в тупике.

У меня есть таблица, которая выглядит так:

AgentID, GoalReached, MonthEndDate
360123, 1, 1/22/2011
360123, Null, 2/22/2011
360123, 1, 3/22/2011
360123, 1, 4/22/2011
360123, 1, 5/22/2011
360123, 2, 6/22/2011
360124, 2, 1/22/2011
360124, 2, 2/22/2011
360124, 1, 3/22/2011
360124, 2, 4/22/2011
360124, 2, 5/22/2011
360124, 3, 6/22/2011

Как мне составить таблицу, которая группируется по агентидам и показывает мне только агентиды, которые достигли уровня 4 из любых 6 последовательных месяцев? А затем покажите, за какой 6-месячный период эта цель была достигнута?

Кроме того, если агент достиг уровня 2 или выше, он также сделал предыдущий уровень 1. Таким образом, если агент имеет три уровня 2 и три уровня 1 за 6-месячный период, то агент достиг только уровня 1.

Конечный результат будет:

AgentID, LevelReached, StartDate, EndDate
360123,1,3/22/2011,6/22/2011
360124,2,2/22/2011,6/22/2011

Спасибо за любые идеи, которые вы можете предоставить.

1 Ответ

2 голосов
/ 02 ноября 2011

Я придумал этот, который выглядит так, как будто он работает:

declare @t table ( AgentID int, GoalReached int, MonthEndDate date)
insert @t (AgentID , GoalReached , MonthEndDate )
values 
(360123, Null, '2/22/2011'),
(360123, 1, '1/22/2011'),
(360123, 1, '3/22/2011'),
(360123, 1, '4/22/2011'),
(360123, 1, '5/22/2011'),
(360123, 2, '6/22/2011'),
(360124, 1, '3/22/2011'),
(360124, 2, '1/22/2011'),
(360124, 2, '2/22/2011'),
(360124, 2, '4/22/2011'),
(360124, 2, '5/22/2011'),
(360124, 3, '6/22/2011')

select  t1.AgentID, t1.GoalReached, t1.MonthEndDate StartDate, max(t2.MonthEndDate) Enddate
from    @t t1
        inner join @t t2 
            on t1.AgentID = t2.AgentID
            and t1.GoalReached = t2.GoalReached
            and datediff(m, t1.MonthEndDate, t2.MonthEndDate) between 0 and 6  -- find any other rows for same agent within 6 months
group by t1.AgentID, t1.GoalReached, t1.MonthEndDate
having count(*) >= 4

Однако я получаю эти строки:

360123  1   2011-01-22  2011-05-22
360124  2   2011-01-22  2011-05-22

Но я думаю, что пример выходных данных, который вы даете, может бытьвыкл.(Конечная дата 6/22 для агента 360123 относится к уровню 2, а не к 1. Для 360124 эта дата относится к уровню 3 вместо 2.

Дайте мне знать, если это удастся. Вы не указаликак обрабатывать случаи, когда, например, существует последовательный период в 9 месяцев, когда цель достигается каждый месяц.Этот запрос подсчитывает перекрывающиеся периоды, например, как два, поэтому дайте мне знать, если, например, строка может быть только в 1 группе.

С уважением, GJ

РЕДАКТИРОВАТЬ

Привет, хорошо, сделал это, см. Ниже.

Чтобы удалить перекрывающиеся последовательности, я использовал рекурсивный CTEЯ думаю, что это единственный способ для группы «узнать», находится ли определенная строка в предыдущей группе. Было бы интересно увидеть другие подходы.

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

Итак, наслаждайтесь!И спасибо за это, милый маленький челюсть; -)

Rgds GJ

declare @t table (AgentID int, GoalReached int, MonthEndDate date)
insert @t (AgentID , GoalReached , MonthEndDate )
values 
(360123, Null, '2/22/2011'),
(360123, 1, '1/22/2011'),
(360123, 1, '3/22/2011'),
(360123, 1, '4/22/2011'),
(360123, 1, '5/22/2011'),
(360123, 2, '6/22/2011'),
(360124, 1, '3/22/2011'),
(360124, 2, '1/22/2011'),
(360124, 2, '2/22/2011'),
(360124, 2, '4/22/2011'),
(360124, 2, '5/22/2011'),
(360124, 3, '6/22/2011'),

(100, 1, '1/1/2010'),
(100, 1, '2/1/2010'),
(100, 2, '3/1/2010'),
(100, 1, '4/1/2010'),
(100, 1, '5/1/2010'),
(100, 1, '6/1/2010'),
(100, 1, '7/1/2010'),
(100, 1, '8/1/2010'),
(100, 1, '9/1/2010')
;


with 
--1: find groups of rows that are within 6 months of eachother and number the rows
step1 as (
select  t1.AgentID, t1.MonthEndDate StartDate, t2.GoalReached, t2.MonthEndDate EndDate,  ROW_NUMBER() over (partition by t1.agentid, t1.MonthEndDate order by t2.monthenddate) RowRank
from    @t t1
        inner join @t t2 
            on t1.AgentID = t2.AgentID
            --and t1.GoalReached = t2.GoalReached
            and datediff(m, t1.MonthEndDate, t2.MonthEndDate) between 0 and 6  -- find any other rows for same agent within 6 months
)
--select * from step1
-- cut sequences off to no longer than 4 rows and get rid of shorter sequences
,step2 as (
    select  * 
    from    step1 t1
    where   exists (
        select *
        from    step1 t2
        where   t1.AgentID = t2.AgentID
                and t1.StartDate = t2.StartDate
                --t1.id1 = t2.id1       -- same group(same start row)
                and t2.RowRank  = 4 -- group has to have at least 4 rows 
                and t1.RowRank <= 4 -- get rid of rows that are beyond 4
    )
)
--select * from step2 
-- collapse groups to a single row (makes next step easier)
,grps as (
    select  AgentID,            
            MAX(GoalReached) MaxGoal,
            MIN(StartDate) StartDate,
            MAX(EndDate) EndDate,
            dense_rank() over (partition by AgentId order by StartDate) GrpRank
    from    step2
    group by agentid,
            StartDate
)
--select * from sub1
-- use common table expression to remove overlap (only way I could figure out how)
,cte as (
    -- anchor to first sequence of 4 rows for each agent
    select  AgentID,
            StartDate,
            EndDate,
            MaxGoal
    from    grps
    where   GrpRank = 1

    union all 

    -- repeat to find following sequences
    select  AgentID,
            StartDate,
            EndDate,
            MaxGoal     
    from    (
                select  g.*, row_number() over (partition by g.AgentId order by g.StartDate) grp_rank
                from    grps g              
                inner join cte c 
                        on  g.AgentID = c.AgentID   -- same agent                       
                        and g.StartDate > c.EndDate -- group must start after previous group has ended (here we remove the overlap)

            )s
    where   s.grp_rank = 1  -- only add 1 group per agent for each iteration of the CTE     
)
select  *
from    cte
order by AgentID, StartDate
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...