Getting average difference between two dates in minutes - sql

I am trying to get avg receiving time in minutes for each outlet.
I have tow tables Orders and ReceivedOrders.
Orders
OrderID OutletID OrderDate
1 1 2017-04-10 17:04:41.000
ReceivedOrders
ReceivingID OrderID ReceivingDate
1 1 2017-04-10 17:06:31.000
i have tried the below query but its reruns zero as avg receiving time
SQL Query
SELECT Outlets.OutletName , avg(datediff(MM, Orders.OrderDate, ReceivedOrders.ReceivingDate)) as Receive
FROM dbo.Orders INNER JOIN
dbo.Outlets ON dbo.Orders.OutletID = dbo.Outlets.OutletID INNER JOIN
dbo.ReceivedOrders ON dbo.Orders.OrderID = dbo.ReceivedOrders.OrderID
group by dbo.Outlets.OutletName
Output
OutletName Receive
Outlet1 0

Use datediff with millisecond option:
select Outlets.OutletName,
avg(datediff(ms, Orders.OrderDate, ReceivedOrders.ReceivingDate)) / 60000 as Receive
from dbo.Orders
inner join dbo.Outlets on dbo.Orders.OutletID = dbo.Outlets.OutletID
inner join dbo.ReceivedOrders on dbo.Orders.OrderID = dbo.ReceivedOrders.OrderID
group by dbo.Outlets.OutletName

Your code is doing exactly what you are specifying. You are getting the date diff in months, not minutes.
When using date parts, just spell out the full name of the date part:
SELECT ol.OutletName,
avg(datediff(minute, o.OrderDate, ro.ReceivingDate)) as Receive
FROM dbo.Orders o INNER JOIN
dbo.Outlets ol
ON o.OutletID = ol.OutletID INNER JOIN
dbo.ReceivedOrders ro
ONo.OrderID = ro.OrderID
GROUP BY ol.OutletName;
The above counts minute boundaries between two values. You may want parts of minutes, in which case I would use a smaller unit. Milliseconds are definitely an option, but they can overflow pretty easily if the dates are even a few months apart. So, you might really want something more like this:
SELECT ol.OutletName,
avg(datediff(second, o.OrderDate, ro.ReceivingDate)/60.0) as minutesToReceive
FROM dbo.Orders o INNER JOIN
dbo.Outlets ol
ON o.OutletID = ol.OutletID INNER JOIN
dbo.ReceivedOrders ro
ONo.OrderID = ro.OrderID
GROUP BY ol.OutletName;
Note the use of 60.0 rather than 60 to force non-integer arithmetic.

Related

why "OR" operator is slower than union in oracle

Does anyone know why an "OR" operator is slower than union in ORACLE.
I have query like this:
Select
O.Order_number,
DA. ID,
DA.Country,
Sum(amount) Amount
from
Order O
left join Delivery_Address DA on
O.ID = DA.order_Id
left join TBL_A on
TBL_A.DA_ID = DA.ID
enter code here
< ... Left joining another 10 tables>
enter code here
Left join Transaction Tr on
TR.Order_id = Order.id
where
DA.Country = 'USA'
OR
Tr.transaction_Date between to_date('20200701','yyyymmdd') and sysdate
This takes 200 secs for the first 50 records.
Select
O.Order_number,
DA.ID,
DA.Country,
Sum(amount) Amount
from
Order O
left join Delivery_Address DA on
O.ID = DA.order_Id
left join TBL_A on
TBL_A.DA_ID = DA.ID
enter code here
< ... Left joining another 10 tables>
enter code here
Left join Transaction Tr on
TR.Order_id = Order.id
where
DA.Country = 'USA'
union
Select
O.Order_number,
DA. ID,
DA.Country,
Sum(amount) Amount
from
Order O
left join Delivery_Address DA on
O.ID = DA.order_Id
left join TBL_A on
TBL_A.DA_ID = DA.ID
enter code here
< ... Left joining another 10 tables>
enter code here
Left join Transaction Tr on
TR.Order_id = Order.id
where
Tr.transaction_Date between to_date('20200701','yyyymmdd') and sysdate
This second query takes 13 secs for the first 50 records.
The transaction_date from the Transaction table is indexed, but the Country column is not indexed.
Anyone have any idea?
The OR allows each subquery to be evaluated independently.
You would have to look at the execution plans to see what is really happening. However, in the first subquery query, an index using da.country is probably using an index. And in the second, tr.transaction_date.

SQL - Tracking Monthly Sales

I am writing a query to summarize sales by month. My problem is that my query only returns records for months with sales. For example, I am looking over a 15 month range. But for one specific part in the example below only 3 of the 15 months had sales.
I'm hoping to have 15 records show up and the other ones have 0's for sales. The reason I am hoping for the additional records is I want to take the standard deviation of this, and dropping records impacts that calculation.
Sample Code:
SELECT I.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(*) as TotalDemand
FROM OrderDetails OD
INNER JOIN Orders O on O.Id = OD.OrderId
INNER JOIN Items I on I.Id = OD.ItemId
WHERE
O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
AND I.PartNumber = '5144831-2'
GROUP BY I.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
Sample Current Output:
Part # | Year | Month | Demand
5144831-2 2017 1 1
5144831-2 2017 2 3
5144831-2 2016 3 1
Desired Output:
I would want an additional row such as:
5144831-2 2016 11 0
To show there were no sales in Nov 2016.
I do have a temp table #_date_array2 with the possible months/years, I think I need help incorporating a LEFT JOIN.
If you want to use left join, you would not be able to use it directly with the inner join. You can do the inner join inside the parenthesis and then do the left join outside to avoid messing with the results of left join. Try this:
SELECT Z.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(Z.OrderId) as TotalDemand
FROM Orders O
LEFT JOIN
(
SELECT OrderId, PartNumber
FROM
OrderDetails OD
INNER JOIN Items I ON I.Id = OD.ItemId
AND I.PartNumber = '5144831-2'
) Z
ON O.Id = Z.OrderId
AND O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
GROUP BY Z.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
To get a count of 0 for months with no order, avoid using count(*) and use count(OrderId) as given above.
Note - You will have to make sure the Orders table has all months and years available i.e. if there is no CreateDate value of, say, November 2016 in the Orders table(left table in the join), the output will also not produce this month's entry.
Edit:
Can you try this:
SELECT Z.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(O.OrderId) as TotalDemand
FROM Orders O
RIGHT JOIN
(
SELECT OrderId, PartNumber
FROM
OrderDetails OD
INNER JOIN Items I ON I.Id = OD.ItemId
AND I.PartNumber = '5144831-2'
) Z
ON O.Id = Z.OrderId
AND O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
GROUP BY Z.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
Assuming you have sales of something in every month, the simplest solution is to switch to conditional aggregation:
SELECT '5144831-2' as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
SUM(CASE WHEN I.PartNumber = '5144831-2' THEN 1 ELSE 0 END) as TotalDemand
FROM OrderDetails OD INNER JOIN
Orders O
ON O.Id = OD.OrderId INNER JOIN
Items I
ON I.Id = OD.ItemId
WHERE O.CreateDate >= '2016-01-01' AND
O.CreateDate <= '2017-03-31'
GROUP BY YEAR(O.CreateDate) , MONTH(O.CreateDate);
Note: This is something of a hack for solving the problem. More robust solutions involve generating the dates and using LEFT JOIN (or similar functionality). However, this is often the fastest way to get the result.
based on all of your comments on other posts etc it seems like you have a table that has a date range you want and you want to be able to run the analysis for multiple/all of the part numbers. So the main issue is you will need a cartesian join between your date table and partnumbers that were sold during that time in order to accomplish you "0"s when not sold.
;WITH cteMaxMinDates AS (
SELECT
MinDate = MIN(DATEFROMPARTS(CreateDateYear,CreateDateMonth,1))
,MaxDate = MAX(DATEFROMPARTS(CreateDateYear,CreateDateMonth,1))
FROM
#_date_array2
)
;WITH cteOrderDetails AS (
SELECT
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
FROM
#_date_array2 d
INNER JOIN Orders o
ON d.CreateDateMonth = MONTH(o.CreateDate)
AND d.CreateDateYear = YEAR(o.CreateDate)
INNER JOIN OrderDetails od
ON o.Id = od.OrderId
INNER JOIN Items i
ON od.ItemId = i.Id
AND i.PartNumber = '5144831-2'
)
, cteDistinctParts AS (
SELECT DISTINCT PartNumber
FROM
cteOrderDetails
)
SELECT
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
,COUNT(od.PartNumber) as TotalDemand
FROM
#_date_array2 d
CROSS JOIN cteDistinctParts p
LEFT JOIN cteOrderDetails od
ON d.CreateDateYear = od.CreateDateYear
AND d.CreateDateMonth = od.CreateDateMonth
AND p.PartNumber = od.PartNumber
GROUP BY
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
To get ALL part numbers simply remove AND i.PartNumber = '5144831-2' join condition.

sql select records between today date plus 16 hours

I am trying to get the records between today order date plus 16 hours.
OrderDate data type is datetime
i have records in database between that period but is not showing using the below query.
For example i have the below records.
2017-03-05 10:20:30.000
2017-03-06 10:20:30.000
query should return the second record.
any suggestions to solve this issue?
SQL Query
SELECT
dbo.Areas.AreaName,
dbo.Brands.BrandName,
dbo.Orders.OrderID,
dbo.Orders.OrderStatus,
dbo.Customers.CustomerName,
dbo.Customers.CustomerID,
dbo.Customers.Phone,
dbo.Customers.Mobile,
dbo.Orders.OrderDate,
dbo.Outlets.OutletName,
dbo.Users.FirstName,
dbo.Users.LastName,
dbo.Sources.SourceName,
SUM((OrderDetails.Quantity * OrderDetails.UnitPrice)) + Orders.DeliveryCharge - ((SUM((OrderDetails.Quantity * OrderDetails.UnitPrice)) + Orders.DeliveryCharge) * Orders.Discount / 100) AS Amount,
ReceivedOrders.ReceivingDate
FROM dbo.Orders
INNER JOIN dbo.Customers
ON dbo.Orders.CustomerID = dbo.Customers.CustomerID
INNER JOIN dbo.Outlets
ON dbo.Orders.OutletID = dbo.Outlets.OutletID
INNER JOIN dbo.Users
ON dbo.Orders.UserID = dbo.Users.UserID
INNER JOIN dbo.Sources
ON dbo.Orders.SourceID = dbo.Sources.SourceID
INNER JOIN dbo.OrderDetails
ON dbo.OrderDetails.OrderID = dbo.Orders.OrderID
INNER JOIN dbo.Brands
ON dbo.Brands.BrandID = dbo.Outlets.BrandID
INNER JOIN dbo.Areas
ON dbo.Areas.AreaID = dbo.Customers.AreaID
LEFT JOIN dbo.ReceivedOrders
ON dbo.ReceivedOrders.OrderID = dbo.Orders.OrderID
WHERE BETWEEN OrderDate AND DATEADD(HOUR, 16, OrderDate) and OrderDate=GETDATE()
GROUP BY dbo.Orders.OrderID,
dbo.Orders.OrderStatus,
dbo.Customers.CustomerName,
dbo.Customers.CustomerID,
dbo.Customers.Phone,
dbo.Customers.Mobile,
dbo.Orders.OrderDate,
dbo.Outlets.OutletName,
dbo.Users.FirstName,
dbo.Users.LastName,
dbo.Sources.SourceName,
dbo.Orders.DeliveryCharge,
Orders.Discount,
dbo.Brands.BrandName,
dbo.Areas.AreaName,
ReceivedOrders.ReceivingDate
ORDER BY dbo.Orders.OrderID
Try this:
GETDATE() BETWEEN Orders.OrderDate AND DATEADD(HOUR, 16, Orders.OrderDate)
use this to get today's date instead of just getdate() which returns present time
select cast(cast(getdate() as varchar(12)) as datetime)
You can use CONVERT(VARCHAR(8),GETDATE(),112) instead of GETDATE() this will remove current time and convert the date to varchar with ISO format yyyyMMdd which doesn't need to be converted to datetime again
WHERE OrderDate BETWEEN CONVERT(VARCHAR(8),GETDATE(),112) AND DATEADD(HOUR, 16,CONVERT(VARCHAR(8),GETDATE(),112) )
Read more about sql date formats Here

SELECT records with condition that filters the last chronilogical multiple and specific value of a column

I have a joined table that looks like that:
my goal is to filter all records that was created after the last 'active' value inside LineStatusName Column. (the yellow marked rows in the attached image).
here is what i have done so far, it is almost work as desired, but the problem is that the date that returns from the nested select steatment is not the date of the highest chronological datetime value of 'active' and if i try to do ORDER BY Changes.ChangeDateTim in the end of the nested select i get a syntax error:
Conversion failed when converting the nvarchar value '30-9000241' to data type int.
I will be grateful if someone can suggest a better solution to achieve that task or to improve my query.
SELECT Orders.OrderID,LineStatuses.LineStatusName,OrderTypes.OrderTypeName,
Changes.ChangeDateTime,Orders.ProjectNumber,Changes.Comments,Changes.ChangeTypeID
FROM Orders
INNER JOIN Changes ON Changes.ItemID = Orders.OrderID
INNER JOIN LineStatusSettings ON LineStatusSettings.LineStatusSettingID = Changes.NewValue
INNER JOIN LineStatuses ON LineStatuses.LineStatusID= LineStatusSettings.LineStatusID
INNER JOIN OrderTypes ON OrderTypes.OrderTypeID = LineStatusSettings.OrderTypeID
WHERE Orders.OrderID = 194 AND Orders.Deleted=0
AND
Changes.ChangeDateTime > (
SELECT TOP 1 Changes.ChangeDateTime
FROM Orders
INNER JOIN Changes ON Changes.ItemID = Orders.OrderID
INNER JOIN LineStatusSettings ON LineStatusSettings.LineStatusSettingID = Changes.NewValue
INNER JOIN LineStatuses ON LineStatuses.LineStatusID= LineStatusSettings.LineStatusID
INNER JOIN OrderTypes ON OrderTypes.OrderTypeID = LineStatusSettings.OrderTypeID
WHERE LineStatuses.LineStatusName = 'active'
) AND OrderTypes.OrderTypeName NOT IN ('disconnected line')
ORDER BY Changes.ChangeDateTime
Here is one method:
with jt as (
<your query here>
)
select jt.*
from jt
where jt.date > (select max(jt2.date)
from jt jt2
where jt2.orderid = jt.orderid and jt2.linestatusname = 'Active'
);

Query to get the customers active in Aug month and inactive in Sep month in SQL

Please help me to find the customers who were active in the month of aug 15 (in terms of transaction ) and inactive in SEP 15.
The query is taking more than 45 min ..Kindly help ..
SELECT DISTINCT C.CustomerCode--,x.CustomerCode
FROM Customer.Customer(nolock) c
INNER JOIN Customer.Card (nolock)cd ON c.CustomerId=cd.CustomerId
INNER JOIN Trans.vwValidRawTransactions rt (nolock) ON rt.AccountNumber=cd.CardNumber AND rt.AccountTypeId=3
where c.CustomerCode not in (
SELECT DISTINCT ca.customercode
FROM Customer.customer Ca (nolock)
INNER JOIN Customer.Card cd (nolock) ON ca.CustomerId=cd.CustomerId
INNER JOIN trans.vwValidRawTransactions ra (nolock) ON cd.CardNumber=ra.AccountNumber AND ra.AccountTypeId=3 AND ra.IsLive=1
WHERE Ra.TransactionDate>='01-09-2015' AND Ra.TransactionDate <'01-10-2015' )
and rt.TransactionDate>='01-08-2015' and rt.TransactionDate<'01-09-2015'
Try using between,
SELECT DISTINCT C.CustomerCode,
x.CustomerCode
FROM Customer.Customer(NOLOCK) c
INNER JOIN Customer.Card (NOLOCK)cd
ON c.CustomerId = cd.CustomerId
INNER JOIN Trans.vwValidRawTransactions rt(NOLOCK)
ON rt.AccountNumber = cd.CardNumber
AND rt.AccountTypeId = 3
WHERE c.CustomerCode NOT
IN (SELECT DISTINCT ca.customercode
FROM Customer.customer Ca(NOLOCK)
INNER JOIN Customer.Card cd(NOLOCK)
ON ca.CustomerId = cd.CustomerId
INNER JOIN trans.vwValidRawTransactions ra(NOLOCK)
ON cd.CardNumber = ra.AccountNumber
AND ra.AccountTypeId = 3
AND ra.IsLive = 1
WHERE Ra.TransactionDate BETWEEN '01-09-2015' AND '01-10-2015')
AND rt.TransactionDate BETWEEN '01-08-2015' AND '01-09-2015'
Your question is a bit unclear because your query is way more complicated than what the question asks.
If you are looking at the transaction table, you can do what you want using aggregation and a having clause:
SELECT rt.AccountNumber
FROM Trans.vwValidRawTransactions rt (nolock)
WHERE rt.AccountTypeId = 3 AND
rt.TransactionDate >= '2015-08-01' AND
rt.TransactionDate < '2015-10-01'
GROUP BY rt.AccountNumber
HAVING MONTH(MAX(rt.TransactionDate)) = 8;
That is, take transactions from the two months. Then choose customers where the month of the maximum date is august. These customers have transactions in August, but not September.
Note the use of ISO standard date formats. You should use standard date formats in SQL code.
I'm not sure what the rest of your query is supposed to be doing.
Try this
SELECT DISTINCT C.CustomerCode--,x.CustomerCode
FROM Customer.Customer(nolock) c
INNER JOIN Customer.Card (nolock)cd ON c.CustomerId=cd.CustomerId
INNER JOIN Trans.vwValidRawTransactions rt (nolock)
ON rt.AccountNumber=cd.CardNumber AND rt.AccountTypeId=3 AND
((rt.IsLive =1 and rt.TransactionDate BETWEEN '01-08-2015' AND '01-09-2015')
or (rt.IsLive !=1 and rt.TransactionDate BETWEEN '01-09-2015' AND '01-10-2015'))