2010-09-28 7 views
3

J'espère que ce n'est pas une dupe d'une autre question, mais je ne pouvais pas le voir ailleurs - c'est aussi une version simplifiée de another question I asked, j'espère pouvoir commencer à travailler sur comment l'aborder.Série de dates consécutives

Je cherche à établir des plages de paiements consécutifs où il y a eu au moins un paiement par mois.

je les données d'exemple suivantes

CREATE TABLE #data 
(
Contact_reference NVARCHAR(55) 
,Date_payment DATETIME 
,Payment_value MONEY 
) 

INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2003-06-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2004-06-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2004-12-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-04-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-05-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-06-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-07-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-08-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-09-08',12.82) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-10-10',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-11-10',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2005-12-10',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2006-01-10',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2006-02-10',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2006-02-28',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2006-04-12',12.8205) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2006-05-10',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-06-11',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-07-10',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-08-09',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-09-10',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-10-09',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-11-09',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2007-12-10',19.2308) 
INSERT INTO #data VALUES ('18EC3CD2-3065-4FF4-BE40-000004228590','2008-01-10',19.2308) 

Et ce que je voudrais être en mesure de le faire est de travailler pour chaque contact les plages sur lesquelles ils ont donné consécutivement (définis comme donnant au moins une fois tous les mois civil), le nombre de paiements consécutifs, la valeur totale par gamme (et idéalement si possible l'écart entre la fourchette actuelle et la fin de la plus récente).

Pour les données de test ci-dessus ma sortie devrait ressembler à ceci:

CREATE TABLE #results 
(
contact_reference NVARCHAR(55) 
,Range_start DATETIME 
,Range_end DATETIME 
,Payments INT 
,Value MONEY 
,months_until_next_payment INT --works out the gap between the range_end date for a group and the range_start date for the next group 
) 
INSERT INTO #results VALUES('18EC3CD2-3065-4FF4-BE40-000004228590','2003-06-08','2003-06-08',1,12.82,12) 
INSERT INTO #results VALUES('18EC3CD2-3065-4FF4-BE40-000004228590','2004-06-08','2004-06-08',1,12.82,6) 
INSERT INTO #results VALUES('18EC3CD2-3065-4FF4-BE40-000004228590','2004-12-08','2004-12-08',1,12.82,4) 
INSERT INTO #results VALUES('18EC3CD2-3065-4FF4-BE40-000004228590','2005-04-08','2006-02-28',12,153.843,2) 
INSERT INTO #results VALUES('18EC3CD2-3065-4FF4-BE40-000004228590','2006-04-12','2008-06-06',27,416.6673,NULL) 

J'ai cherché des réponses à l'aide des îles ou des itérations mais je franchement ne savent même pas où commencer à les appliquer à mon question, donc toute aide massivement apprécié :)

Répondre

4

Edit: J'ai ajouté dans la colonne months_until_next_payment. Cela serait fait plus efficacement dans l'application plutôt qu'avec une auto-jointure, car SQL Server n'a aucun moyen particulièrement satisfaisant de référencer les rangées suivantes et précédentes.

;WITH base AS ( 
SELECT Contact_reference , 
      Payment_value, 
      DATEPART(YEAR, Date_payment)*12 + DATEPART(MONTH, Date_payment) - 
       DENSE_RANK() OVER 
        (PARTITION BY Contact_reference 
        ORDER BY DATEPART(YEAR, Date_payment)*12 + DATEPART(MONTH, Date_payment)) AS G, 
      Date_payment 
FROM  #data 
), 
cte AS 
(
SELECT 
      Contact_reference, 
      ROW_NUMBER() over (partition by Contact_reference 
           order by MIN(Date_payment)) RN, 
      MIN(Date_payment) Range_start, 
      MAX(Date_payment) Range_end, 
      COUNT(Payment_value) Payments, 
      SUM(Payment_value) Value 
FROM base 
GROUP BY Contact_reference, G 
) 
SELECT 
     c1.Contact_reference, 
     c1.Payments, 
     c1.Range_end, 
     c1.Range_start, 
     c1.Value, 
     DATEDIFF(month, c1.Range_end,c2.Range_start) months_until_next_payment 
FROM cte c1 
LEFT join cte c2 ON c1.Contact_reference=c2.Contact_reference and c2.RN = c1.RN+1 
+1

Désolé pour l'ignorance pure de cela, mais pourquoi faites-vous DATEPART (année) * 12? et aussi si vous pouviez me faire savoir comment commencer à faire la jointure gauche pour le paiement months_until_next qui serait fantastique, comme je suis un bon n00b avec SQL! :) Merci! – Dibstar

+0

Au lieu d'une autre jointure, vous pouvez simplement ajouter une colonne calculée dans l'instruction select finale, en la modifiant pour lire: SELECT Contact_reference, MIN (paiement_date) Range_start, MAX (paiement_date) Range_end, COUNT (Payment_value) Payments, SUM (Payment_value) Value , (sélectionnez MIN (G) - qg + 1 de cte1 où g> qg) comme mois_until_next_payment FROM cte1 q GROUP BY Contact_reference, G –

+3

DATEPART (année) * 12 + DATEPART (mois) est utilisé pour obtenir un entier qui changements par un chaque fois que nous avançons un mois civil. C'est parce qu'il y a 12 ans dans un mois, quand nous sommes remis à janvier, nous soustrayons 11 pour le changement du mois, et ajoutons 1 * 12 pour le changement de l'année, signifiant que le résultat net en ajoute un. –

1

Vous pouvez le faire en utilisant le curseur. La langue comme C#/java est un meilleur choix pour ce problème.

DECLARE @date DATETIME 
DECLARE @nextDate DATETIME 
DECLARE @rangeStart DATETIME 
DECLARE @rangeEnd DATETIME 

DECLARE @value decimal(18,2) 
DECLARE @valueSum decimal(18,2) 
DECLARE @count int 

DECLARE @PaymentCursor CURSOR 

SET @PaymentCursor = CURSOR FOR 
    SELECT Date_payment, Payment_value FROM #data 
    ORDER BY Date_payment 

OPEN @PaymentCursor 
    FETCH NEXT FROM @PaymentCursor INTO @nextDate, @value 
    SET @date = @nextDate 
    SET @rangeStart = @nextDate 
    SET @valueSum = 0 
    SET @count = 0 

    WHILE (@@FETCH_STATUS = 0) 
    BEGIN  
     FETCH NEXT FROM @PaymentCursor INTO @nextDate, @value 

     SET @count = @count + 1 
     SET @valueSum = @valueSum + @value  

     IF (DATEDIFF(mm, @date, @nextDate) > 1) 
     BEGIN 
      SELECT @rangeStart AS RangeStart, @date AS RangeEnd, @count AS Coount, @valueSum AS VALUE, DATEDIFF(mm, @date, @nextDate) AS months_until_next_payment 
      SET @valueSum = 0    
      SET @count = 0 
      SET @rangeStart = @nextDate 
     END 

     SET @date = @nextDate   
    END 

    SELECT @rangeStart AS RangeStart, @date AS RangeEnd, @count AS Coount, @valueSum AS VALUE, null AS months_until_next_payment 

CLOSE @PaymentCursor 
DEALLOCATE @PaymentCursor 
+0

Si vous devez utiliser un curseur dans SQL, vous devriez probablement envisager de placer la logique dans votre application plutôt que dans la base de données. – JNK

+0

@JNK Oui, je suis d'accord. – Branimir

+0

merci pour cela, mais il produit chaque ligne comme une table de résultats séparée (et je veux tous ensemble pour une référence plus facile) et il semble courir très lentement (a pris environ 6 minutes pour tirer 10 enregistrements, et j'ai une bonne quelques centaines de milliers). – Dibstar

Questions connexes