Een veelgestelde vraag van klanten in de Business Intelligence-wereld is “hoeveel klanten zijn we dit jaar kwijtgeraakt?” Bedrijven willen dit weten om sneller in te grijpen, zodat klanten zaken met ze blijven doen. In dit artikel help ik je SQL te maken die dit aantal berekent. Op internet vond ik meer artikelen, maar meestal leggen die uit hoe je dit in MDX doet. Ik hou echt niet van MDX. Het is ingewikkeld en moeilijk te begrijpen. Ik hou van OLAP-kubussen, maar ik hou het ook graag simpel. Met simpel bedoel ik: alle logica in SQL, KPI’s in SQL berekenen en ze wegschrijven in een tabel.
De eerste stap bij het berekenen van verloren klanten is bepalen wat een verloren klant is. Bijvoorbeeld: een verloren klant is een klant die dit kalenderjaar geen enkele factuur heeft gekregen, MAAR vorig kalenderjaar wel. Leg deze definitie heel precies vast, met tijdvakken en criteria. Wil je misschien een rollende periode van 52 weken voor de factuurtijd? In dit voorbeeld neem ik het kalenderjaar.
Met de definitie hierboven zijn aan het begin van het jaar alle klanten verloren, omdat ze in de eerste maand van het jaar nog geen factuur hebben gekregen. Dat is prima. De lijn daalt naarmate de weken of maanden verstrijken. De uitkomst van de formule ziet er ongeveer zo uit (grafiek gemaakt in SQL Server Reporting Services 2008R2):

De eerste stap is een ijkpunt voor de berekening kiezen. Ik gebruik de eerste dag van de maand. Je kunt ook elke week of elke dag rekenen als je wilt. Dit is belangrijk, want je wilt in je grafiek het aantal verloren klanten per maand of per week tellen.
Dan nu de SQL. Het ziet er misschien wat ingewikkeld uit, maar dat is het niet. Het commentaar is in het Nederlands, maar ik leg de principes hier uit.
Stap 1 - Maak een lijst van alle combinaties van klant en DateID, op basis van de factuurdatum
Stap 2 - Maak een “explosie” van alle combinaties van klant en eerste dag van de maand
Stap 3- Combineer stap 1 en stap 2 en bepaal of de klant verloren is of niet. We controleren of de klant een factuur kreeg vóór de datum waarop je kijkt, maar in hetzelfde jaar. In de WHERE clause controleren we of hij vorig jaar een factuur kreeg. Zo niet, dan is hij verloren. Klinkt simpel, toch?
Hier komt de code. Heb je vragen, laat dan een reactie achter. Ik schreef deze code op Microsoft Dynamics AX voor een van mijn klanten. Het kostte me misschien 2 uur. Een kleine investering voor de klant, maar erg handig!
WITH KLAS-- Genereer een overzicht van alle datum/klant combinaties (SELECT DISTINCT DK.ACCOUNTNUM AS KlantID , CONVERT(VARCHAR, FH.InvoiceDate, 112) AS DatumID , FH.InvoiceDate AS Datum , DD.Jaar , FR.LINEAMOUNT AS Bedrag FROM dbo.CUSTINVOICETRANS AS FR -- FactuurRegel
INNER JOIN dbo.CUSTINVOICEJOUR AS FH -- FactuurHeader ON (FR.INVOICEID = FH.INVOICEID)
INNER JOIN dbo.CUSTTABLE AS DK -- Klant ON (FH.ORDERACCOUNT = DK.ACCOUNTNUM)
INNER JOIN dbo.SMMBUSRELSALESDISTRICTGROUP AS SD -- SalesDistrict ON (DK.SALESDISTRICTID = SD.SALESDISTRICTID) AND (SD.DATAAREAID = 'pp')
INNER JOIN DimDatum AS DD -- DimDatum ON ( CONVERT(VARCHAR, FH.INVOICEDATE, 112) = DD.DatumId)
INNER JOIN INVENTTABLE AS DP -- DimProduct ON (FR.ITEMID = DP.ITEMID)
WHERE FR.DATAAREAID = 'pp' AND FH.DATAAREAID = 'pp' AND DK.DATAAREAID = 'pp' AND DP.DATAAREAID = 'pp' )
-- Genereer een lijst van alle klanten en eerste dag van maand-combinaties-- voor alle dagen in het huidige jaar en in het vorige jaar.-- Later gaan we voor deze datums bepalen of de klant verloren is op deze datum, KD -- KlantDatumAS (SELECT DISTINCT ACCOUNTNUM AS KlantID, DK.NAME AS KlantNaam, SD.[DESCRIPTION] AS SalesDistrict, DD.DatumId, DD.DatumWaarde, DD.Jaar FROM CUSTTABLE AS DK INNER JOIN SMMBUSRELSALESDISTRICTGROUP AS SD ON (DK.SALESDISTRICTID = SD.SALESDISTRICTID) CROSS JOIN DimDatum AS DD
WHERE DD.Jaar IN ( YEAR(DATEADD(YY,-1,GETDATE())), YEAR(GETDATE()) ) AND DD.DagNummerVanMaand = 1 AND DD.DatumId <= CONVERT( VARCHAR, GETDATE(), 112) -- evt aanzetten als je het hele jaar wilt tonen, ook de toekomst -- AND DD.Jaar <= YEAR(GETDATE())
)
SELECT DISTINCT KLD.KlantID , KlantNaam , KLD.SalesDistrict , KLD.DatumId , DD.Jaar , DD.MaandNummerVanJaar , DD.MaandCode , (CASE WHEN KL.KlantID IS NULL THEN 1 ELSE 0 END) AS IsVerlorenFROM KD AS KLD-- Met deze join controleer je of de klant in het huidige jaar gefactureerd is op een tijdstip-- die voor de datum ligt waarop je kijkt. Als dit niet het geval is dan is de klant op dit moment-- verloren. Op het moment dat hij gefactureerd is dan is hij niet meer verlorenLEFT OUTER JOIN KL AS KL ON KLD.KlantID = KL.KlantID AND KL.DatumID <= KLD.DatumId AND KLD.Jaar = KL.Jaar
INNER JOIN DimDatum AS DD ON (KLD.DatumId = DD.DatumId)
-- Controleer of hij vorig jaar gefactureerd isWHERE EXISTS ( SELECT 1 FROM KL AS VJ WHERE VJ.Jaar= KLD.Jaar-1 AND KLD.KlantID = VJ.KlantID)
ORDER BY 1,2Veelgestelde vragen
Hoe bereken ik verloren klanten in T-SQL?
Leg eerst vast wat een verloren klant is. In het voorbeeld heeft een verloren klant dit kalenderjaar geen factuur gehad, maar vorig kalenderjaar wel. Maak daarna een lijst van klanten per eerste dag van de maand en toets hun facturen aan die datum.
Wat is een verloren klant in een business intelligence-rapport?
Dat hangt af van je definitie, dus leg die vast met tijdvakken en criteria. Het voorbeeld gebruikt kalenderjaren. Je kunt voor de factuurtijd ook een rollende periode van 52 weken nemen.
Waarom lijken aan het begin van het jaar alle klanten verloren?
Met de definitie op kalenderjaar heeft in de eerste maand nog geen klant een factuur. De lijn daalt naarmate de weken of maanden verstrijken en klanten facturen krijgen. Dat is te verwachten en prima.
Kan ik verloren klanten berekenen zonder MDX?
Ja. Houd alle logica in SQL in plaats van MDX, dat ingewikkeld en moeilijk te begrijpen is. Bereken de KPI in SQL en schrijf hem weg naar een tabel, dan heb je voor dit getal geen OLAP-kubus nodig.
Welk ijkpunt gebruik ik om verloren klanten per maand te tellen?
Gebruik de eerste dag van de maand als ijkpunt. Je kunt ook elke week of elke dag rekenen. Kies het interval dat past bij de grafiek waarin je verloren klanten per maand of per week telt.
Reacties
Gerelateerde blogs
De traagste SQL-queries vinden
T-SQL - Een voortschrijdend totaal over 4 weken maken
Leeftijd berekenen in SQL Server