Reputation: 73
So I'm tasked with finding the delay in weeks for each order. I've used the DATEDIFF
function and I'd like to believe I'm on the right track but when I use it I get NULL
as the result. The data type for each column are both date.
SELECT DISTINCT Sales.Orders.custid, Sales.Customers.companyname,
CASE
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) = 7 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 14 THEN '1 Week'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) = 14 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 21 THEN '2 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) = 21 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 28 THEN '3 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) = 28 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 35 THEN '4 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) = 35 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 42 THEN '5 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) = 42 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 49 THEN '6 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) > 49 THEN '7+ Weeks'
ELSE 'Unknown'
END AS Order_Delay
FROM Sales.Orders, Sales.Customers
ORDER BY
Order_Delay ASC;
I'm using MS SQL Server Management Studio 2016.
Upvotes: 1
Views: 922
Reputation: 9299
https://learn.microsoft.com/en-us/sql/t-sql/functions/datediff-transact-sql?view=sql-server-2017
DATEDIFF ( datepart , startdate , enddate )
My guess is that in your system shippeddate
is mostly later or equal to orderdate
, so instead of
FLOOR(DATEDIFF(DAY, o.shippeddate, o.orderdate) / 7)
you might want
FLOOR(DATEDIFF(DAY, o.orderdate, o.shippeddate) / 7)
to check this assumption you might want to add ca.Order_Delay
(as per code suggested by @Used_by_already) to list of selected columns and see what values are there. My bet is they are all negative.
Upvotes: 1
Reputation: 37377
Try rewriting your query like this:
SELECT DISTINCT Sales.Orders.custid, Sales.Customers.companyname,
CASE
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) >= 7 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 14 THEN '1 Week'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 21 THEN '2 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 28 THEN '3 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 35 THEN '4 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 42 THEN '5 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 49 THEN '6 Weeks'
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) >= 49 THEN '7+ Weeks'
ELSE 'Unknown'
END AS Order_Delay
FROM Sales.Orders, Sales.Customers
ORDER BY
Order_Delay ASC;
I think you want to check whether difference is in particular range (from 7 to 14, etc.).
So I corrected first cndition:
WHEN DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) >= 7 AND DATEDIFF(DAY, Sales.Orders.shippeddate, Sales.Orders.orderdate) < 14 THEN '1 Week'
You could not use BETWEEN
here, as its range includes also broders of a set.
For other cases, you don't need to check whether the difference is greater than, eg. in second WHEN
you know that the difference is >=14
, since it failed first condition, etc.
Upvotes: 2
Reputation: 35593
FROM Sales.Orders, Sales.Customers
is an old-fashioned way of forming a cross join
. This is probably accidental but the impact of this can be awful both in terms of performance but also the results can be plain wrong - and they are in your example. It is for this reason that I always recommend you use explicit join syntax such as inner join
and cease using commas as a way to define the from clause.
You simply have to properly join the 2 tables, otherwise every order is applied to every customer and the results would be quite wrong. I have guessed that join, but it should look something like the one seen below:
SELECT /* DISTINCT ?? */
Sales.Orders.custid
, Sales.Customers.companyname
, CASE
WHEN ca.Order_Delay >= 7 THEN '7+ Weeks'
WHEN ca.Order_Delay >= 1 AND ca.Order_Delay < 7 THEN CAST(ca.Order_Delay AS varchar) + ' Weeks'
ELSE 'Unknown'
END AS order_delay
FROM Sales.Orders AS o
INNER JOIN Sales.Customers AS c ON o.custid = c.id
CROSS APPLY (
SELECT
FLOOR(DATEDIFF(DAY, o.shippeddate, o.orderdate) / 7)
) ca (order_delay)
ORDER BY
order_delay ASC
;
In SQL Server is possible to use cross apply
as a way to perform a calculation, and give that calculation an alias that you can then use in the select clause. This can have the effect of making your code somewhat easier to read, but this is optional.
Above I have suggested a way to use floor()
which you should read about here:
https://learn.microsoft.com/en-us/sql/t-sql/functions/floor-transact-sql?view=sql-server-2017
nb: If you want to show data for unshipped orders then you may need to change to an outer apply
, and if an order is unshipped the the datediff()
function would return NULL and your case expression
would need to explicitly cater for NULLs
SELECT /* DISTINCT ?? */
Sales.Orders.custid
, Sales.Customers.companyname
, CASE
WHEN ca.Order_Delay >= 7 THEN '7+ Weeks'
WHEN ca.Order_Delay >= 1 AND ca.Order_Delay < 7 THEN CAST(ca.Order_Delay AS varchar) + ' Weeks'
WHEN ca.Order_Delay IS NULL then 'Unshipped'
ELSE 'Unknown'
END AS order_delay
FROM Sales.Orders AS o
INNER JOIN Sales.Customers AS c ON o.custid = c.id
OUTER APPLY (
SELECT
FLOOR(DATEDIFF(DAY, o.shippeddate, o.orderdate) / 7)
) ca (order_delay)
ORDER BY
order_delay ASC
;
Upvotes: 2
Reputation: 454
You are looking for exactly 7, 14, 21 etc. You need >= 7 instead (and repeat for the rest...).
Upvotes: 0