2013-02-12 4 views
1

Étant donné un tableau de voitures et leur odomètre à différentes dates (le premier de chaque mois), comment puis-je écrire TSQL (idéalement, pour une utilisation en vue SQL Server) pour retourner les valeurs "incrémentales"? En d'autres termes, je veux l'opération inverse de Calculate a Running Total in SQL Server enCalcul des valeurs décompactées dans TSQL?

Exemple:

Sur ce tableau:

 
CarId | Date | Mileage 
--------------------------- 
    1 1/1/2000 10000 
    1 2/1/2000 11000 
    1 3/1/2000 12000 
    2 1/1/2000 10000 
    2 2/1/2000 11001 
    2 3/1/2000 12001 
    3 1/1/2000 10000 
    (missing datapoint for (3, 2/1/2000)) 
    3 3/1/2000 12000 

Nous reviendrions quelque chose comme (les détails/cas de pointe sont flexibles):

 
CarId | Date | Delta 
--------------------------- 
    1 1/1/2000 10000 
    1 2/1/2000 1000 
    1 3/1/2000 1000 
    2 1/1/2000 10000 
    2 2/1/2000 1001 
    2 3/1/2000 1000 
    3 1/1/2000 10000 
    3 3/1/2000 2000 

Répondre

1

approche même que celui de @Richard Deeming, mais celui-ci concerne les valeurs nulles possibles comme inclus dans la question initiale.

;with cte (rn, id, date, mileage) 
as 
(
    select 
    row_number() over (partition by id order by id, date) 
    , id 
    , date 
    , mileage 
    from 
     cars 
    where 
     mileage is not null 
) 
select 
    "current".id 
    , "current".date 
    , delta = isnull("current".mileage - predecessor.mileage, "current".mileage) 
from 
    cte as "current" 
    left join cte as predecessor 
    on "current".id = predecessor.id 
    and "current".rn - 1 = predecessor.rn 

Voir SQL-Fiddle.

+0

Je supposais que "datapoint manquant" signifiait qu'il n'y avait pas une ligne dans la table, pas qu'il y avait une ligne mais le kilométrage était "NULL". –

+0

peut-être un point de vue-chose. Comme indiqué dans votre réponse, je me suis référé à base-data-insert vu dans l'une des autres réponses. – Nico

+0

Dans mon cas en particulier, nous avons des enregistrements avec des zéros qui devraient être ignorés. –

2

Cela devrait fonctionner pour SQL 2005 ou plus:

WITH cteData As 
(
    SELECT 
     CarId, 
     Date, 
     Mileage, 
     ROW_NUMBER() OVER (PARTITION BY CarId ORDER BY Date) As RowNumber 
    FROM 
     dbo.Cars 
) 
SELECT 
    C.CarId, 
    C.Date, 
    CASE 
     WHEN P.CarId Is Null THEN C.Mileage 
     ELSE C.Mileage - P.Mileage 
    END As Delta 
FROM 
    cteData As C 
    LEFT JOIN cteData As P 
    ON P.CarId = C.CarId 
    And P.RowNumber = C.RowNumber - 1 
ORDER BY 
    C.CarId, 
    C.Date 
; 

SQL Fiddle

NB: Cela suppose que "manque pour point de données (3, 2/1/2000)" signifie qu'il n'y a pas de ligne dans la table pour la voiture 3, Février 2000.

+0

cette requête renvoie en réalité de mauvais résultats pour la voiture 3 – Nico

+0

@Nico: Vraiment? Je viens de l'essayer, et ça me semble bien: http://www.sqlfiddle.com/#!3/d41d8/8780 –

+0

Cela dépend des données de base. J'ai couru votre requête contre les données fournies par tamago via une requête d'insertion (voir ci-dessous). Si la valeur NULL existe, la requête ne renvoie pas de résultats corrects (ou peut-être pas les résultats estimés;)) - s'il n'y a aucune ligne dans les données de base, la requête fonctionne correctement! – Nico

1

fonctions de fenêtre sont génial. Mais SQL Server n'a pas celui que vous avez besoin jusqu'à ce que SQL Server 2012. Là-bas, vous avez la fonction de décalage:

select t.*, 
     (Milage - lag(Milage) over (partition by carId order by date)) as Delta 
from t 

Pour les versions antérieures, vous pouvez utiliser une sous-requête corrélative:

[problèmes pour télécharger la requête] hélas.

select t.*, (Mileage - prevMileage) as Delta 
    from (select t.*,  
       (select top 1 Mileage from t t2 
        where t2.carId = t.carId and t2.date < t.date order by desc 
        ) as prevDelta 
     from t 
    ) t 
+0

Je m'excuse. Chaque fois que j'essaie de mieux formater cette requête, StackOverflow me donne une erreur. –

1

Essayer de le faire sans dépendance à l'égard des 2012 fonctions, curseur, tandis que la boucle, etc.

Cela fonctionne à l'intérieur certaines limites - à savoir, l'hypothèse nulle d'entrée pour l'entrée de la voiture n ° 3 est un problème pour elle:

DECLARE @cars table ([id] int, [date] smalldatetime, [mileage] int) 
INSERT INTO @cars ([id], [date], [mileage]) 
SELECT 1, '1/1/2000', 10000 UNION ALL 
SELECT 1, '2/1/2000', 11000 UNION ALL 
SELECT 1, '3/1/2000', 12000 UNION ALL 
SELECT 2, '1/1/2000', 10000 UNION ALL 
SELECT 2, '2/1/2000', 11000 UNION ALL 
SELECT 2, '3/1/2000', 12000 UNION ALL 
SELECT 3, '1/1/2000', 10000 UNION ALL 
SELECT 3, '2/1/2000', NULL UNION ALL 
SELECT 3, '3/1/2000', 12000 


SELECT t1.id, t1.date, t1.mileage, t2.id, t2.date, t2.mileage, t1.mileage - t2.mileage as miles FROM @cars t1 
LEFT JOIN @cars t2 
ON t1.id = t2.id 
AND t1.date = DATEADD(MONTH,1, t2.date) 
Questions connexes