6

fellow developers and analysts. I have some experience in SQL and have resorted to similar posts. However, this is slightly more niche. Thank you in advance for helping.

I have the below dataset (edited. Apology)

Data Set

Setup

CREATE TABLE CustomerPoints
(
    CustomerID INT,
    [Date]     Date,
    Points     INT
)

INSERT INTO CustomerPoints
VALUES
    (1, '20150101', 500),
    (1, '20150201', -400),
    (1, '20151101', 300),
    (1, '20151201', -400)

and need to turn it into (edited. The figures in previous table were incorrect) enter image description here

Any positive amount of points are points earned whereas negative are redeemed. Because of the FIFO (1st in 1st out concept), of the second batch of points spent (-400), 100 of those were taken from points earned on 20150101 (UK format) and 300 from 20151101.

The goal is to calculate, for each customer, the number of points spent within x and y months of earning. Again, thank you for your help.

Sweepy Dodo
  • 1,761
  • 9
  • 15
  • Thank you Alex for copying data here. – Charles Bretana Oct 13 '16 at 14:13
  • 1
    Please include ALL the data. it's hard to determine what is going on when only some data is in the table, and you mention the other (points earned in 2015 which are not in table) data in the text of the question – Charles Bretana Oct 13 '16 at 14:16
  • 4
    Your data and redeemed values still don't match up. It's hard to help if we are confused too. Also, can you show what queries you have done so far to get your results. – SS_DBA Oct 13 '16 at 14:22
  • @WEI_DBA He's suggesting that the -400 attributed to January 12th, 2015 borrowed 300 points from January 11th and 100 points from January 1st. Likewise the -400 attributed to January 2nd borrowed 400 points from January 1st. Edit: I assumed January due to mention regarding UK dates, but result query suggest the dates are months. The logic surrounding the numbers still applies. – Derrick Moeller Oct 13 '16 at 14:29
  • 1
    Even after editing it is wrong. Your redeemed points in result do not match up with the data – Pரதீப் Oct 13 '16 at 14:31
  • `The goal is to calculate, for each customer, the number of points spent within x and y months of earning` <= of earning what exactly? First time ever (minimum date) per customer? So column 2 is everything that falls up to 6 months after first ever earned record (by date) and 3rd column everything from 6 months to 1 year after first ever earned record (by date again)?? – Igor Oct 13 '16 at 14:37
  • I am terribly sorry, Charles Bretana, WEI_DBA and Prdp. I updated the tables and can confirm they are final. – Sweepy Dodo Oct 13 '16 at 14:39
  • So how are you coming up with 700 in the 2nd column? I am no mathematician but I do not see any combination of your sample data that would result in 700. Your values currently are `500, -400, 300, -400`. – Igor Oct 13 '16 at 14:44
  • 1
    @T.Fung - Explain the logic with example to get that numbers – Pரதீப் Oct 13 '16 at 14:45
  • Dear Igor, it is relative to when points were earned, not 1st ever per customer. In the case of the -400 on 20151201, 100 of those were aged 11m and 300 aged 1m – Sweepy Dodo Oct 13 '16 at 14:47
  • Sorry but at this point I am going to have to vote to close the question as it is unclear even after 30 minutes of editing. I will retract my vote if you can edit your question with the logic (pseudo code if you want) that describes how each resulting number is formed in your output. As it is written now now one (but you) seems to understand what you are asking for. – Igor Oct 13 '16 at 14:50
  • @T.Fung - Dude, Explain the logic for `700` in `0-6months` in result – Pரதீப் Oct 13 '16 at 14:56
  • @Prdp 400 points were spent on 20150201 which were 1 month old relative to 20150101 Of the 400 points spent on 20151201, 100 were taken from 20150101 (leftover from previous spend). These 100 points were aged 11 months relative to spend date (20151201). The other 300 were taken from 20151101 which at the time of spend was aged 1 month. – Sweepy Dodo Oct 13 '16 at 15:01
  • @T.Fung - Still you didn't explain how 700 came in result. – Pரதீப் Oct 13 '16 at 15:04
  • @Prdp 400 points (entire 1st spend) + 300 points (part of 2nd spend) – Sweepy Dodo Oct 13 '16 at 15:14
  • Nope, I am still lost. Hopefully someone (else) gets it. Don't get me wrong, I understand that you are tracking additions / subtractions on the date they occurred but the logic for how you are aggregating that data in your output completely eludes me. – Igor Oct 13 '16 at 15:19
  • `400 points (entire 1st spend) + 300 points (part of 2nd spend) ` <= `-` entries are spends, positive entries are additions. So if part 1 spend is 400 (entry 02-01) then part 2 spend is also 400 as these are both negative (subtraction) entries. If this is the case and you expect 700 maybe you made a mistake and put the `-` in front of the wrong number in your sample data. – Igor Oct 13 '16 at 15:24
  • Thank you, Igor. It is more complicated than just (-400) + (-400) because not the entire 2nd spend of 400 points shall go into the '0-6 Month' column because 100 of the 400 points spent came from 20150101 as the spend that occurred on 20150102 gave a 100 remaining balance. – Sweepy Dodo Oct 13 '16 at 15:37
  • Hi @T.Fung, I got your intention and I'll write you an answer tomorrow. – David דודו Markovitz Nov 20 '16 at 23:49

1 Answers1

5

I have already answered a similar question here and here

You need to explode points earned and redeemed by single units and then couple them, so each point earned will be matched by a redeemed point.

For each of these matching rows calculate the months elapsed from the earning to the redeeming and then aggregate it all.

For FN_NUMBERS(n) it is a tally table, look at other answers I have linked above.

;with
p as (select * from CustomerPoints),
e as (select * from p where points>0),
r as (select * from p where points<0),
ex as (
    select *, ROW_NUMBER() over (partition by CustomerID order by [date] ) rn
    from e
    join FN_NUMBERS(1000) on N<= e.points
),
rx as (
    select *, ROW_NUMBER() over (partition by CustomerID order by [date] ) rn
    from r
    join FN_NUMBERS(1000) on N<= -r.points
),
j as (
select ex.CustomerID, DATEDIFF(month,ex.date, rx.date) mm
from ex
join rx on ex.CustomerID = rx.CustomerID and ex.rn = rx.rn and rx.date>ex.date
)
-- use this select to see points redeemed in current and past semester
select * from j  join (select 0 s union all select 1 s ) p on j.mm >= (p.s*6)+(p.s) and j.mm < p.s*6+6 pivot (count(mm) for s in ([0],[2])) p order by 1, 2

-- use this select to see points redeemed with months detail
--select * from j pivot (count(mm) for mm in ([0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])) p order by 1

-- use this select to see points redeemed in rows per month
--select CustomerID, mm, COUNT(mm) PointsRedeemed   from j  group by CustomerID, mm order by 1

output of default query, 0 is 0-6 months, 1 is 7-12 (age of redemption in months)

CustomerID  0   1
1           700 100

output of 2nd query, 0..12 is the age of redemption in months

CustomerID  0   1   2   3   4   5   6   7   8   9   10  11  12
1           0   700 0   0   0   0   0   0   0   0   0   100 0

output from 3rd query, is the age of redemption in months

CustomerID  mm  PointsRedeemed
1           1   700
1           11  100

bye

Community
  • 1
  • 1
MtwStark
  • 3,866
  • 1
  • 18
  • 32
  • Stark, many many thanks for this solution and apology for the belated reply. I have input dummy data into the script and run. It worked like a charm. I'm now in the process of parsing and understanding it. Again, thank you Stark – Sweepy Dodo Feb 07 '17 at 09:45
  • You are welcome. Glad to know, if you need explanation, feel free to ask. – MtwStark Feb 07 '17 at 11:21
  • I will absolutely. Thank you, Stark – Sweepy Dodo Feb 07 '17 at 14:22