2016-11-09 4 views
3

Ich verwende SQL Server 2012, um rollierende Verkaufsinformationen für Handelsagenten zu extrahieren. Diese Agenten erhalten Boni, wenn sie innerhalb einer rollierenden Zeitspanne von 6 oder weniger Tagen 15 Verkäufe erzielen. Wenn sie ein Ziel setzen, wird der Rollzähler zurückgesetzt. Sonntags sollte ignoriert werden.Verkaufsziele mit laufenden Summen mit Zurücksetzungen SQL Server 2012

So angesichts der folgenden AgentID, Datum und Daten Umsatz:

SELECT 1 AgentID,'2016-10-31' Date,1 Sales 
INTO #Sales 
UNION SELECT 1,'2016-11-01',2 
UNION SELECT 1,'2016-11-02',1 
UNION SELECT 1,'2016-11-03',5 
UNION SELECT 1,'2016-11-04',3 
UNION SELECT 1,'2016-11-05',2 
UNION SELECT 1,'2016-11-07',6 
UNION SELECT 1,'2016-11-08',5 
UNION SELECT 1,'2016-11-09',4 
UNION SELECT 1,'2016-11-10',6 
UNION SELECT 1,'2016-11-11',1 
UNION SELECT 1,'2016-11-12',3 
UNION SELECT 1,'2016-11-14',2 
UNION SELECT 1,'2016-11-15',2 
UNION SELECT 1,'2016-11-16',4 
UNION SELECT 1,'2016-11-17',2 
UNION SELECT 1,'2016-11-18',2 

die Daten, die ich das Ziel erwarten sind getroffen werden:

2016-11-07 (period 2016-11-01 -> 2016-11-07) 
2016-11-10 (period 2016-11-08 -> 2016-11-10) 
2016-11-18 (period 2016-11-12 -> 2016-11-18) 

AgentID Date Sales Qualify 
------------------------------- 
1 2016-10-31 1 0 
1 2016-11-01 2 0 
1 2016-11-02 1 0 
1 2016-11-03 5 0 
1 2016-11-04 3 0 
1 2016-11-05 2 0 
1 2016-11-07 6 1 
1 2016-11-08 5 0 
1 2016-11-09 4 0 
1 2016-11-10 6 1 
1 2016-11-11 1 0 
1 2016-11-12 3 0 
1 2016-11-14 2 0 
1 2016-11-15 2 0 
1 2016-11-16 4 0 
1 2016-11-17 2 0 
1 2016-11-18 2 1 

ich ein paar Ansätze ausprobiert habe, aber ich kann keine Möglichkeit finden, die Rollsummen zurückzusetzen.

Ich denke, dass Fensterfunktionen sind der Weg zu gehen.

auf Beiträge der Suche wie Window Functions - Running Total with reset

ich denke, das ähnlich ist, was ich brauche, kann aber nicht ganz bekommen es richtig zu arbeiten.

UPDATE: Das erste, was ich ausprobiert habe, war rollende 6-Tage-Fenster zu erstellen, aber ich sehe nicht, dass dies in einem Set-basierten Ansatz funktioniert. Ich könnte einen Cursor verwenden, um durch diese Zeilen zu gehen, aber ich mag die Idee wirklich nicht.

SELECT DATEADD(DAY,-6,a.Date) StartDate,Date EndDate,a.AgentID,a.Sales, 
(SELECT SUM(b.Sales) 
    FROM cteSales b 
    WHERE b.Date <= a.Date 
    AND b.Date >= DATEADD(DAY,-6,a.Date)) TotalSales 
FROM cteSales a 

Ich habe dann versucht, das Skript in der URL erwähnt zu verwenden oben, aber ich verstehe nicht wirklich, was es tut. Ich ändere nur Dinge in der Hoffnung, über die Lösung zu stolpern und das funktioniert einfach nicht.

WITH c1 as 
(
    select *, 
    sum(sales) over(order by IDDate rows unbounded preceding) as rt 
    from cteSales 
) 

SELECT date, sales, rt, 
    SalesTarget_rt - lag(SalesTarget_rt, 1, 0) over(order by date) as SalesTarget, 
    rt * SalesTarget_rt as new_rt 

from c1 
    cross apply(values(case when rt >= 15 then 1 else 0 end)) as a1(SalesTarget_rt); 
+0

haben Sie die Skripte, die Sie bisher versucht haben? – iamdave

+1

Werden die Verkäufe nach 15 am selben Tag auch auf das nächste Ziel angerechnet? Also, wenn sie 14 am Tag 1 und dann 3 am Tag 2 verkauften, beginnt die Zählung für das nächste Ziel am Tag 3 bei 2 oder 0? – iamdave

+0

Oh und auch (!) Gibt es immer nur einen Rekord pro Tag für jeden Agenten? – iamdave

Antwort

1

Richtig dann! Das war eine lustige Herausforderung und ich bin sehr zufrieden, dass ich es geknackt habe. Hinweise usw. sind in den Code-Kommentaren enthalten. Wenn Sie die Anzahl der Tage ändern möchten, innerhalb derer ein Bonus angesammelt werden kann, ändern Sie den Wert in @DaysInBonusPeriod. Dies funktioniert auch für mehr AgentID s und eine Reihe von Daten, unter der Annahme, dass die fehlenden Daten werden nicht in dem Bonusabgrenzungszeitraum enthält - dh: Wenn Sie Sonntag ignorieren und Mittwoch wird die Zeit so gezählt:

Day  Period Day 
Monday 1 
Tuesday 2 
Thursday 3 
Friday 4 
Saturday 5 
Monday 6 

Lösung

declare @t table(AgentID int 
       ,DateValue Date 
       ,Sales int 
       ); 
insert into @t     
select 1,'2016-10-31',1 union all 
select 1,'2016-11-01',2 union all 
select 1,'2016-11-02',1 union all 
select 1,'2016-11-03',5 union all 
select 1,'2016-11-04',3 union all 
select 1,'2016-11-05',2 union all 
select 1,'2016-11-07',6 union all 
select 1,'2016-11-08',5 union all 
select 1,'2016-11-09',4 union all 
select 1,'2016-11-10',6 union all 
select 1,'2016-11-11',1 union all 
select 1,'2016-11-12',3 union all 
select 1,'2016-11-14',2 union all 
select 1,'2016-11-15',2 union all 
select 1,'2016-11-16',4 union all 
select 1,'2016-11-17',2 union all 
select 1,'2016-11-18',2 union all 

select 2,'2016-10-31',1 union all 
select 2,'2016-11-01',7 union all 
select 2,'2016-11-02',0 union all 
select 2,'2016-11-03',0 union all 
select 2,'2016-11-04',0 union all 
select 2,'2016-11-05',0 union all 
select 2,'2016-11-07',0 union all 
select 2,'2016-11-08',0 union all 
select 2,'2016-11-09',1 union all 
select 2,'2016-11-10',3 union all 
select 2,'2016-11-11',2 union all 
select 2,'2016-11-12',3 union all 
select 2,'2016-11-14',7 union all 
select 2,'2016-11-15',6 union all 
select 2,'2016-11-16',3 union all 
select 2,'2016-11-17',5 union all 
select 2,'2016-11-18',3; 

-- Set the number of days that sales can accrue towards a Bonus. 
declare @DaysInBonusPeriod int = 6; 

with rn -- Derived table to get incremental ordering for recursice cte. This is useful as Sundays are ignored. 
as 
(
    select t.AgentID 
      ,t.DateValue 
      ,t.Sales 
      ,row_number() over (order by t.AgentID, t.DateValue) as rn 
    from @t t 
) 
,prev -- Using the row numbering above, find the number of sales in the day before the bonus accrual period. We have to use the row numbers as Sundays are ignored. 
as 
(
     select t.AgentID 
       ,t.DateValue 
       ,t.Sales 
       ,t.rn 
       ,isnull(tp.Sales,0) as SalesOnDayBeforeCurrentPeriod 
     from rn t 
      left join rn tp 
       on(t.AgentID = tp.AgentID 
        and tp.rn = t.rn - @DaysInBonusPeriod  -- Get number of sales on the day before the max Bonus period. 
        ) 
) 
,cte -- Use a recursive cte to calculate running totals based on sales, whether the bonus was achieved the previous day and if the previous bonus was more than 5 days ago. 
as 
(
    select rn 
      ,AgentID 
      ,DateValue 
      ,Sales 
      ,SalesOnDayBeforeCurrentPeriod 
      ,Sales as TotalSales 
      ,case when Sales >= 15 then 1 else 0 end as Bonus 
      ,1 as DaysSinceLastBonus 

    from prev 
    where rn = 1 -- Select just the first row in the dataset. 

    union all 

    select t.rn 
      ,t.AgentID 
      ,t.DateValue 
      ,t.Sales 
      ,t.SalesOnDayBeforeCurrentPeriod 

      -- If the previous row was for the same agent and not a bonus, add the day's sales to the total, subtracting the sales from the day before the 6 day bonus period if it has been more than 6 days since the last bonus. 
      ,case when t.AgentID = c.AgentID 
       then case when c.Bonus = 0 
         then t.Sales + c.TotalSales - case when c.DaysSinceLastBonus >= @DaysInBonusPeriod then t.SalesOnDayBeforeCurrentPeriod else 0 end 
         else t.Sales 
         end 
       else t.Sales 
       end as TotalSales 

      -- If the value in the TotalSales field above is 15 or more, flag a bonus. 
      ,case when 
        case when t.AgentID = c.AgentID                            --\ 
        then case when c.Bonus = 0                             -- \ 
          then t.Sales + c.TotalSales - case when c.DaysSinceLastBonus >= @DaysInBonusPeriod then t.SalesOnDayBeforeCurrentPeriod else 0 end -- \ Same statement 
          else t.Sales                              --/as TotalSales 
          end                                 --/
        else t.Sales                                --/ 
        end >= 15 
       then 1 
       else 0 
       end as Bonus 

      -- If there is no flag in Bonus field above, increment the number of days since the last bonus. 
      ,case when 
       case when                                   --\ 
         case when t.AgentID = c.AgentID                            -- \ 
         then case when c.Bonus = 0                             -- | 
           then t.Sales + c.TotalSales - case when c.DaysSinceLastBonus >= @DaysInBonusPeriod then t.SalesOnDayBeforeCurrentPeriod else 0 end -- | 
           else t.Sales                              -- \ Same statement 
           end                                 --/as Bonus 
         else t.Sales                                -- | 
         end >= 15                                 -- | 
        then 1                                   --/
        else 0                                   --/ 
        end = 0 
       then c.DaysSinceLastBonus + 1 
       else 0 
       end as DaysSinceLastBonus 

    from prev t 
     inner join cte c 
      on(t.rn = c.rn+1) 
) 
select AgentID 
     ,DateValue 
     ,Sales 
     ,TotalSales 
     ,Bonus 
from cte 
order by rn 
option (maxrecursion 0); 
+0

Großartige Arbeit iamdave. Ich dachte, es wäre komplex und sah den Code, sogar mehr als ich erkannte Ich bekomme eine rekursive Grenze für die CTEs, aber ich bin mir sicher, dass ich das umgehen kann, entweder durch die Erhöhung der Grenze oder in kleinere aufteilen Chucks. Ich werde noch mehr Tests mit meinen Live-Daten durchführen, aber es sieht so aus, als würde es funktionieren. – Bob

+1

@Bob Keine Sorgen :) Fügen Sie einfach die 'maxrecursion' Option am Ende, nach meinem aktualisierten Skript. "0" lässt es bis zum Ende der Daten laufen und jeder Wert ungleich Null lässt es bis zu dieser Anzahl von Rekursionen laufen und löst einen Fehler aus, wenn es getroffen wird. – iamdave

Verwandte Themen