sql difference in two dates - sql

In sql server, I have two tables:
Tran_Ex
Transactions
They both have customer_id which is the key to join the tables.
I want to find the difference in WORKING DAYS of the Date_Reported (from transactions) from the Date_Received (from the Tran_ex). I would like an extra column with these figures:
eg
Date Reported | Date Received | Difference in days
Thanks in advance

Use DATEDIFF() function()
You can get Working Day (Monday to Friday) difference from this query, for bank holidays you need seperate logic.
Select Date_Reported,
Date_Received ,
(DATEDIFF(dd, Date_Reported, Date_Received) + 1)
-(DATEDIFF(wk, Date_Reported, Date_Received) * 2)
-(CASE WHEN DATENAME(dw, Date_Reported) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, Date_Received) = 'Saturday' THEN 1 ELSE 0 END)
AS Working_days_Difference
from Tran_Ex as tx
inner join
Transactions as tr
on(tx.customer_id = tr.customer_id)
Modified the Query on suggestions based on #scsimon for not using shorthands.
SELECT Date_Reported,
Date_Received ,
datediff(day,((CASE WHEN Datename(weekday, Date_Reported) = 'Sunday' THEN 1 ELSE 0 END ) - (CASE WHEN Datename(weekday, Date_Received ) = 'Saturday' THEN 1 ELSE 0 END )),Datediff(day,(Datediff(week, Date_Reported, Date_Received ) * 2 ),
(Datediff(day, Date_Reported, Date_Received ) + 1 )))
AS Working_days_Difference
from Tran_Ex as tx
inner join
Transactions as tr
on(tx.customer_id = tr.customer_id)

Use DATEDIFF() function :
select t.Date_Reported, t1.Date_Received,
datediff(day, t.Date_Reported, t1.Date_Received) [Difference in days]
from Tran_Ex tx
inner join Transactions t on t.customer_id = tx.customer_id;

Related

Return 1 row with various sums based on date?

Assume a table of purchase transactions with columns CustId, Amount, DatePosted where Amount is the value of the transaction, and DatePosted is a DATETIME value. Given a specific CustId, how would I write a select such that it returns a single row with the following columns: CustId, total value of transactions in the last 3 days, last 60 days, 1 year, 2 years (5 columns total).
Example table:
CustId
Amount
DatePosted
1234
698.02
2023-01-23Z12:34:56
1234
582.69
2022-12-15Z19:57:23
1234
7775.22
2022-12-02Z02:34:32
1234
18.72
2022-01-23Z12:34:56
1234
2.27
2021-01-23Z12:34:56
Expected output given the sample data above when searching using CustId=1234:
CustId
3-day Total
60-day Total
1 year Total
2 year Total
1234
698.02
9055.93
9074.65
9076.92
You could get all purchase data for the last 2 years, then using SUM with SQL CASE expression to calculate total value for each time-range.
SELECT
CustId,
SUM(CASE WHEN DatePosted >= Last3Day THEN Amount ELSE 0 END) AS [3-day Total],
SUM(CASE WHEN DatePosted >= Last60Day THEN Amount ELSE 0 END) AS [60-day Total],
SUM(CASE WHEN DatePosted >= Last1Year THEN Amount ELSE 0 END) AS [1 year Total],
SUM(CASE WHEN DatePosted >= Last2Year THEN Amount ELSE 0 END) AS [2 year Total]
FROM
<your data table>,
(SELECT
DATEADD(DAY, -3, GETDATE()) AS Last3Day,
DATEADD(DAY, -60, GETDATE()) AS Last60Day,
DATEADD(YEAR, -1, GETDATE()) AS Last1Year,
DATEADD(YEAR, -2, GETDATE()) AS Last2Year) timerange
WHERE DatePosted >= Last2Year
GROUP BY CustId;
Demo: http://sqlfiddle.com/#!18/9eecb/179880
This query assumes 2 year max. If you want to go further back then change the where clause as well. No need to use coalesce or a derived table. SQL server query planner may be smart enough to provide similar performance for all these solutions but this is easier to understand:
SELECT
CustId,
SUM(CASE WHEN DatePosted >= DATEADD(day, -3, GETDATE()) THEN Amount ELSE 0 END) AS [3-day Total],
SUM(CASE WHEN DatePosted >= DATEADD(day, -60, GETDATE()) THEN Amount ELSE 0 END) AS [60-day Total],
SUM(CASE WHEN DatePosted >= DATEADD(year, -1, GETDATE()) THEN Amount ELSE 0 END) AS [1 year Total],
SUM(Amount) AS [2 year Total]
FROM PurchaseTransactions
WHERE CustId = 1234 AND DatePosted >= DATEADD(year, -2, GETDATE())
GROUP BY CustId
This is set up so that you can set #CustID = null and the query will return results for all customers in the set.
EDIT: Updated my query below to give you more flexibility across your desired ranges should you wish to derive additional heuristics. (Counts, averages, etc.)
Also removed coalesce as it's simply not needed here.
DECLARE #CustID BIGINT;
SELECT table1.custID,
SUM([3Day].amount) AS [3DayTotal],
COUNT([3Day].amount) AS [3DayCount]
SUM([60Day].amount) AS [60DayTotal],
SUM([1Year].amount) AS [1YearTotal],
Sum([2Year].amount) AS [2YearTotal],
AVG([2Year].amount) AS [2YearAverage]
FROM table1 LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(DAY, -3, GETDATE())) AS [3Day] ON table1.CustID = [3Day].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(DAY, -60, GETDATE())) AS [60Day] ON table1.CustID = [60Day].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(YEAR, -1, GETDATE())) AS [1Year] ON table1.CustID = [1Year].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(YEAR, -2, GETDATE()) AS [2Year] ON table1.CustID = [2Year].CustID
WHERE table1.CustID = #CustID
OR #CustID IS NULL
GROUP BY table1.CustID

Difficulty filtering by Select Column

I'm going to apologize in advance. I basically stumble through SQL as projects need done but my knowledge is rather lacking, so I apologize for any incorrect terminology or poor syntax.
I would appreciate it if anyone would be able to help me out.
I have the following query.
WITH BusinessDayCalc
AS
(
SELECT
EstimatedClosingDate AS EstimatedClosingDate
from SampleDB
)
SELECT
a.*,
(DATEDIFF(dd, GETDATE(), EstimatedClosingDate) + 1)
-(DATEDIFF(wk,GETDATE(), EstimatedClosingDate) * 2)
-(CASE WHEN DATENAME(dw, GETDATE()) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, EstimatedClosingDate) = 'Saturday' THEN 1 ELSE 0 END)
-(Select Count(*) FROM Holiday Where Date >= GETDATE() AND Date < EstimatedClosingDate)
AS BusinessDaysUntil
FROM BusinessDayCalc a
Where EstimatedClosingDate > GetDate() AND EstimatedClosingDate < (GetDate()+17)
I have also attached pics of the current Output and the Holiday Table that is being referenced.
My issue is that I would like to be able to filter my data to show any data that is 8 or 12 business days out, however, I am unable to pull through the column name or have SQL recognize the BusinessDaysUntil column.
Would someone be able to help me out? Once I get this squared away, the rest of the project should go smoothly.
You can't use a derived column in the WHERE clause.
Also you are using a rather useless CTE which returns only 1 column of the table SampleDB.
Instead create a CTE with the query and then select from it and filter:
WITH cte AS (
SELECT
EstimatedClosingDate,
(DATEDIFF(dd, GETDATE(), EstimatedClosingDate) + 1)
-(DATEDIFF(wk,GETDATE(), EstimatedClosingDate) * 2)
-(CASE WHEN DATENAME(dw, GETDATE()) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, EstimatedClosingDate) = 'Saturday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, EstimatedClosingDate) = 'Saturday' THEN 1 ELSE 0 END)
-(Select Count(*) FROM Holiday Where Date >= GETDATE() AND Date < EstimatedClosingDate) AS BusinessDaysUntil
FROM SampleDB
WHERE EstimatedClosingDate > GetDate() AND EstimatedClosingDate < (GetDate()+17)
)
SELECT * FROM cte
WHERE BusinessDaysUntil > ?
Replace BusinessDaysUntil > ? with the filter that you want to apply.

Calculate MTD using CTE and Projected Sales in SQL

I am trying to add MTD Sales in a SQL Query. I figured out how to do that with JOINS but i want to use a CTE to calculate MTD sales and then use that to calculate projected_sales.Formula for projected sales is (MTD/wkdaysinmonth*wkdaystodate)[which is also stored in CTE Table). Is there a way to make it easy? I wrote the following code;
Input:
Email PaymentAmount orderdate
xyz#gmail.com 10 11/01/2018
xyz#gmail.com 20 11/09/2018
sample output:
EmailAddress MTD Projected_sales
xyz#gmail.com 30 0.19
where Projected sales is calculated as number of days passed=7 and total number of business days in november 22. {[30/7*22]=0.19} (Present date = 11/09/2018)
with dates as(
select dateadd(d,-day(getdate())+1,convert(date,getdate())) as startofmonth,
dateadd(d,-1,dateadd(m,1,dateadd(d,-day(getdate())+1,convert(date,getdate())))) as endofmonth,
convert(date,getdate()) as today
)
,daycounts as(
select dates.*,
(DATEDIFF(dd, startofmonth, endofmonth) + 1)
-(DATEDIFF(wk, startofmonth, endofmonth) * 2)
-(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, endofmonth) = 'Saturday' THEN 1 ELSE 0 END) as wkdaysinmonth,
(DATEDIFF(dd, startofmonth, today) + 1)
-(DATEDIFF(wk, startofmonth, today) * 2)
-(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, today) = 'Saturday' THEN 1 ELSE 0 END) as wkdaystodate
from dates
)
SELECT DISTINCT Customers.EmailAddress as email,
o1.YTD
FROM
Customers
INNER JOIN
Orders
ON
Orders.CustomerID= Customers.CustomerID
JOIN
(SELECT
c.EmailAddress,
SUM(Orders.PaymentAmount) AS YTD
FROM
Customers c
JOIN
Orders
ON c.CustomerID=Orders.CustomerID
WHERE
Orders.OrderDate BETWEEN '01/01/2018 00:00' AND GETDATE()
GROUP BY
EmailAddress) AS o1 ON o1.EmailAddress = Customers.EmailAddress
WHERE
Orders.OrderDate >= (GETDATE()-7)
You can try to use cte recursive create a calendar table for orderdate startDate to endDate.
Then OUTER JOIN base on calendar table and do condition aggregate function in subquery get workdate.
;WITH cte
AS (SELECT email,
Dateadd(day, 1, Eomonth(Min(orderdate), -1)) minDt,
Dateadd(day, 1, Eomonth(Max(orderdate))) maxDt
FROM t
GROUP BY email
UNION ALL
SELECT email,
Dateadd(day, 1, mindt),
maxdt
FROM cte
WHERE Dateadd(day, 1, mindt) < maxdt),
cte2
AS (SELECT *,
Count(CASE
WHEN Datename(dw, t1.mindt) NOT IN ('Sunday', 'Saturday' )
THEN
1
END) OVER( ORDER BY t1.mindt) workdt
FROM cte t1)
SELECT t1.email,
t2.total,
Max(diffdt) / ( Max(workdt) * Max(workdtmax) * 1.0 ) Projected_sales
FROM (SELECT *,
Max(workdt)
OVER(
partition BY email
ORDER BY workdt DESC) workdtMax,
Datediff(day, Min(mindt) OVER(partition BY email ORDER BY workdt)
, Max(mindt) OVER(partition BY email ORDER BY workdt DESC)) + 1 diffdt
FROM cte2) t1
LEFT JOIN (SELECT email,
Sum(paymentamount) total,
Min(orderdate) minDt,
Max(orderdate) maxDt
FROM t
GROUP BY email) t2
ON t1.mindt BETWEEN t2.mindt AND t2.maxdt
AND t1.email = t2.email
WHERE t2.total IS NOT NULL
GROUP BY t1.email,
t2.total
sqlfiddle
Reuslt
email total Projected_sales
xyz#gmail.com 30 0.19480519480519
You can generate a "calendar" table that has the weekdays for each day in the month.
Your calculation for the projected doesn't make sense to me. So, I've also included what I consider to be a better calculation:
with dates as (
select distinct dte,
(case when datename(weekday, dte) not in ('Saturday', 'Sunday') then 1 else 0 end) as num_weekdays,
dte as month_start
from t cross apply
(values (dateadd(day, 1 - day(orderdate), orderdate))) v(dte)
union all
select dateadd(day, 1, d.dte),
(case when datename(weekday, dte) not in ('Saturday', 'Sunday') then 1 else 0 end) + num_weekdays,
d.month_start
from dates d
where dte < dateadd(day, -1, dateadd(month, 1, month_start))
),
d as (
select d.*, max(num_weekdays) over (partition by month_start) as month_weekdays
from dates d
)
select d.month_start, t.email,
sum(paymentamount) as mtd,
sum(paymentamount) * max(month_weekdays) / max(d.num_weekdays) as my_projected,
sum(paymentamount) * 1.0 / (max(month_weekdays) * max(d.num_weekdays)) as your_projected
from t join
d
on t.orderdate = d.orderdate
group by d.month_start, t.email;
Here is a db<>fiddle.

T-SQL Get 3 Month Interval : Trying to count Property_IDs over a close_dt Interval

Trying to count Property_IDs over a close_dt Interval
select distinct pd.state AS StateName, zw.CountyName AS [County Name]
,sum(case when pc.close_dt >= dateName(MM,dateadd(MM,-3,GetDate()))then 1
else 0 end) AS [0-3 Months]
,sum(case when pc.close_dt >= dateName(MM,dateadd(MM,-6,GetDate()))
and pc.close_dt < dateName(MM,dateadd(MM,-3,GetDate())) then
1 else 0 end) AS [3-6 Months]
from resnet_mysql.dbo.property_details pd (Nolock)
join resnet.dbo.ZipCodesView zw (nolock)
on CAST(LEFT(pd.zip, 5) AS varchar) = CAST(zw.ZipCodeID AS varchar)
join resnet_mysql.dbo.property_closings pc (nolock)
on pd.property_id = pc.property_id
group by pd.state, zw.countyName, pc.close_dt
How can I get the 3 month interval from the previous 3 month interval value? So it will be 3-6 months?
I want it to look like this.
But I get this error.
I am thinking you want something like this:
select pd.state AS StateName, zw.CountyName AS [County Name],
sum(case when pc.close_dt >= dateadd(month, -3, GetDate()) then 1
else 0
end) AS [0-3 Months]
sum(case when pc.close_dt >= dateadd(month, -6, GetDate()) and
pc.close_dt < dateadd(month, -3, GetDate())
then 1
else 0
end) AS [3-6 Months]
from resnet_mysql.dbo.property_details pd join
resnet.dbo.ZipCodesView zw
on LEFT(pd.zip, 5) = CAST(zw.ZipCodeID as VARCHAR(5)) join
resnet_mysql.dbo.property_closings pc
on pd.property_id = pc.property_id
group by pd.state, zw.countyName;
Your original code has so many errors, it is hard to list them:
DATENAME() returns a string. Why would you want to compare that to a date?
You are aggregating based on date ranges. You don't want to include the date in the GROUP BY.
LEFT() already returns a string; there is no need to convert it.
You probably don't want to compare a string version of zip code to a numeric id. But if you do, the conversion should specify the length.
WITH (NOLOCK) is not recommended unless you actually know what you are doing.

SQL - Average on DateDiff with taking out weekends

I have an SQL statement that calculates how many days late our vendors pay for invoices. It only includes weekdays and it works perfectly. My customer is now asking if I can have an average of those days late per vendor. Here is my code that works:
DATEDIFF(dd, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE))
-(DATEDIFF(wk, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) * 2)
-(CASE WHEN DATENAME(dw, PURCHTABLE.CONFIRMEDDLV) = 'Sunday'
THEN 1
ELSE 0
END)
-(CASE WHEN DATENAME(dw, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) = 'Saturday'
THEN 1
ELSE 0
END) AS 'DAYS LATE'
If I try to put this in an AVG(), but it tells me I cannot perform an aggregate function on an aggregate function.
UPDATE:
This is my original SQL:
SELECT
PURCHTABLE.PURCHNAME AS 'VENDOR NAME',
PURCHTABLE.ORDERACCOUNT AS 'VENDOR NUMBER',
COUNT(DISTINCT PURCHTABLE.PURCHID) AS 'PURCHASE ORDER',
COUNT(PURCHLINE.LINENUMBER) AS 'NUMBER OF LINES',
SUM(PURCHLINE.LINEAMOUNT) AS 'PO PRICE TOTAL',
DATEDIFF(dd, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE))-(DATEDIFF(wk, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) * 2)-(CASE WHEN DATENAME(dw, PURCHTABLE.CONFIRMEDDLV) = 'Sunday' THEN 1 ELSE 0 END)-(CASE WHEN DATENAME(dw, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) = 'Saturday' THEN 1 ELSE 0 END) AS 'DAYS LATE'
FROM
PURCHTABLE
JOIN
PURCHLINE ON PURCHLINE.PURCHID = PURCHTABLE.PURCHID
JOIN
VENDPACKINGSLIPJOUR ON VENDPACKINGSLIPJOUR.PURCHID = PURCHTABLE.PURCHID
WHERE
PURCHTABLE.DELIVERYDATE >= '2017-01-01'
AND
PURCHTABLE.DELIVERYDATE <= '2017-01-20'
AND
PURCHTABLE.ORDERACCOUNT = 'VN03526'
GROUP BY
PURCHTABLE.PURCHNAME,
PURCHTABLE.ORDERACCOUNT,
PURCHTABLE.DELIVERYDATE,
PURCHTABLE.CONFIRMEDDLV
So I don't quite understand how I change that to what you are saying.....I'm tried this but it's not working.
SELECT
PURCHTABLE.PURCHNAME AS 'VENDOR NAME',
PURCHTABLE.ORDERACCOUNT AS 'VENDOR NUMBER'
FROM
(SELECT
PURCHTABLE.PURCHNAME AS 'VENDOR NAME',
PURCHTABLE.ORDERACCOUNT AS 'VENDOR NUMBER',
COUNT(DISTINCT PURCHTABLE.PURCHID) AS 'PURCHASE ORDER',
COUNT(PURCHLINE.LINENUMBER) AS 'NUMBER OF LINES',
SUM(PURCHLINE.LINEAMOUNT) AS 'PO PRICE TOTAL',
DATEDIFF(dd, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE))-(DATEDIFF(wk, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) * 2)-(CASE WHEN DATENAME(dw, PURCHTABLE.CONFIRMEDDLV) = 'Sunday' THEN 1 ELSE 0 END)-(CASE WHEN DATENAME(dw, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) = 'Saturday' THEN 1 ELSE 0 END) AS 'DAYS LATE'
FROM
PURCHTABLE
JOIN
PURCHLINE ON PURCHLINE.PURCHID = PURCHTABLE.PURCHID
JOIN
VENDPACKINGSLIPJOUR ON VENDPACKINGSLIPJOUR.PURCHID = PURCHTABLE.PURCHID
WHERE
PURCHTABLE.DELIVERYDATE >= '2017-01-01'
AND
PURCHTABLE.DELIVERYDATE <= '2017-01-20'
AND
PURCHTABLE.ORDERACCOUNT = 'VN03526') A
GROUP BY
PURCHTABLE.PURCHNAME,
PURCHTABLE.ORDERACCOUNT,
PURCHTABLE.DELIVERYDATE,
PURCHTABLE.CONFIRMEDDLV
The code you have at the moment will be part of a larger query, such as:
select VendorID
,VendorName
...
...
,<Your Days Late Code> as DaysLate
,InvoiceAmount
...
from tables
group by VendorID
,VendorName
all you need to do to average over all of this is pick the other columns you want to do the average by and wrap the whole thing in another select statement:
select VendorID
,VendorName
,avg(DaysLate) as AverageDaysLate
,sum(InvoiceAmount) as TotalInvoiceAmount
from(
select VendorID
,VendorName
...
...
,<Your Days Late Code> as DaysLate
,InvoiceAmount
...
from tables
group by VendorID
,VendorName
) a
group by VendorID
,VendorName