web-dev-qa-db-fra.com

Calculez le nombre moyen de jours entre les commandes

Je voudrais calculer le nombre moyen de jours entre plusieurs dates de commande à partir d'un tableau appelé Commandes. Pour chaque CustomerID, quel est le nombre moyen de jours entre les commandes. Le tableau exemple est comme ci-dessous ( img ):

CREATE TABLE #Orders(CustomerID int, OrderDate datetime);

INSERT #Orders(CustomerID, OrderDate) VALUES
    (100,'20170114'),(100,'20170123'),(100,'20170129'),
    (101,'20170202'),(101,'20170212');

J'ai essayé cette requête:

SELECT  CustomerID, AVG(OrderDate - PriorDate)
FROM (SELECT CustomerID, OrderDate
           , LAG(OrderDate) OVER (PARTITION BY CustomerID ORDER BY OrderDate) as PriorDate
      FROM #Orders where CustomerID = 100)

Cependant, cela donne:

Msg 102, niveau 15, état 1
Syntaxe incorrecte près de ')'.

4
Zahir

Plusieurs problèmes:

  • Vous essayez de prendre une moyenne par client, mais vous ne regroupez pas par client.
  • Les calculs implicites pour les datetimes (OrderDate-PriorDate) Ne sont pas une bonne idée ( essayez cela avec date ou datetime2 ) - utilisez DATEDIFF.
  • La division entière ne vous donnera pas une moyenne comme vous le pensez - essayez SELECT 5/2;. Vous devez convertir au moins une entrée en décimale, soit implicitement (*1.0) Ou explicitement (CONVERT(), TRY_CONVERT(), CAST(), etc.). Explicit vous permet de contrôler les décimales dans certains cas.
  • LAG() n'était pas terminée - alors que la valeur par défaut est 1, je pense qu'il est bon d'être explicite que vous voulez la ligne précédente.
  • L'erreur de syntaxe incorrecte provient en fait de la fin de la requête - lorsque vous avez quelque chose comme SELECT ... FROM (<subquery>), cette sous-requête doit être nommée, vous devez donc utiliser quelque chose comme SELECT ... FROM (<subquery>) AS x; par exemple .

Essayez ce qui suit:

SELECT 
  CustomerID, 
  AvgLag = AVG(CONVERT(decimal(7,2), DATEDIFF(DAY, PriorDate, OrderDate)))
FROM
(
  SELECT CustomerID, OrderDate, PriorDate = LAG(OrderDate,1) 
    OVER (PARTITION BY CustomerID ORDER BY OrderDate)
  FROM #Orders
  WHERE CustomerID = 100
) AS lagged
GROUP BY CustomerID;

Résultats:

CustomerID    AvgLag
----------    ------
100           7.50

Bien sûr, si vous voulez la moyenne pour tous les clients, laissez simplement la clause WHERE. Mais si vous ne voulez vraiment qu'un seul client, vous n'avez pas vraiment besoin du client dans la sortie, vous pouvez donc ajuster un peu la requête pour vous débarrasser du GROUP BY (Paramétrons l'ID client tout en nous y sommes):

DECLARE @CustomerID int = 100;

SELECT AvgLag = AVG(Lag), CustomerID = @CustomerID -- you may not need this
FROM 
(
  SELECT Lag = CONVERT(decimal(7,2), DATEDIFF(DAY, LAG(OrderDate,1) 
    OVER (PARTITION BY CustomerID ORDER BY OrderDate), OrderDate))
  FROM #Orders
  WHERE CustomerID = @CustomerID
) AS Lagged;
6
Aaron Bertrand

Un moyen de simplifier la requête - si vous n'avez besoin que du délai moyen entre les dates de commande - consiste à identifier que vous n'avez besoin que des dates des première et dernière commandes et du nombre de commandes pour chaque client. Si vous avez 11 commandes pour un client et un an entre la première et la dernière commande, la moyenne est de 365 / 10.

SELECT 
  CustomerID, 
  AvgLag = CASE WHEN COUNT(*) > 1 THEN 
                    CONVERT(decimal(7,2), 
                            DATEDIFF(day, MIN(OrderDate), MAX(OrderDate)))
                    / CONVERT(decimal(7,2), COUNT(*) - 1)
                ELSE NULL
           END
FROM #Orders
GROUP BY CustomerID ;
5
ypercubeᵀᴹ

Je pense que c'est fondamentalement la même chose que ypercube +1 (je ne l'ai pas vu)

select CustomerID
     , cast(DATEDIFF(dd, min(OrderDate), max(OrderDate)) as decimal) / (count(*) - 1) as [avgDayDelta], count(*)
from #Orders
group by CustomerID
having count(*) > 1
0
paparazzo