Finding the difference in dates in a single field - sql

So I have the following columns in a table in our database here is an example.....
Transaction_Date | Trans_Code
1/1/2015 | JR
1/15/2015 | CP01
So I am trying to find the difference between the invoice date (JR) and the payment date (CP01). What is the best way to go about this? I tried to do a sub select but that didn't work.
Here is something else I tried with the below suggestion
Select
INVC_NUMB,
CUST_NUMB,
DATEDIFF (
DAY,
MAX(CASE WHEN TRANS_CODE = 'JR' THEN CONVERT(CHAR(10), DATEADD(d, TRANS_RDAT +5843, 0), 101) END),
MAX(CASE WHEN TRANS_CODE = 'CP01' THEN CONVERT(CHAR(10), DATEADD(d, TRANS_RDAT +5843, 0), 101) END)
) AS DAYDIFF
FROM AR20
WHERE CONVERT(CHAR(10), DATEADD(d, TRANS_RDAT +5843, 0), 112) > 20141231
GROUP BY
INVC_NUMB,
CUST_NUMB,
CONVERT(CHAR(10), DATEADD(d, TRANS_RDAT +5843, 0), 1)
ORDER BY
CONVERT(CHAR(10), DATEADD(d, TRANS_RDAT +5843, 0), 1)

One method is conditional aggregation:
select datediff(day,
max(case when trans_code = 'JR' then transaction_date end),
max(case when trans_code = 'CP01' then transaction_date end)
) as diff
from t;

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

Divide by Case When

I'm currently doing some work on a 6 month rolling report, but i seem to be a little bit stuck on one area.
I have one column Liveatcutoff_PC it will be populated by 1 for live or 0 for not live. If it's 0 it means it's cancelled. I want to work out the cancellation % for the date range in my code but I'm not having much luck.
SELECT
UserID
,Agent
,COUNT(CASE WHEN SaleDate>=DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-6, 0)
AND SaleDate < DATEADD(MONTH, DATEDIFF(MONTH, -6, GETDATE())-6, 0)
AND LiveAtCutOff_PC='0' THEN LiveAtCutOff_PC END)
FROM PDS_SALES_PMI
WHERE SaleDate>='2019-01-01'
AND Leaver='0'
GROUP BY UserID, Agent
Help much appreciated.
If I understand correctly, you can use AVG():
AVG(CASE WHEN SaleDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-6, 0) AND
SaleDate < DATEADD(MONTH, DATEDIFF(MONTH, -6, GETDATE())-6, 0) THEN LiveAtCutOff_PC * 1.0
END) as LiveAtCutoff_PC_ratio
Your question specifies that LiveAtCutOff_PC has values of 0 and 1, which implies that the field is a number. However, the code compares it to a string, which suggests otherwise. If the values are only 0 and 1 then the above should work for both numbers and strings (although for bit you would need to convert the value).
So since you requirement is to find out the percentage of canceled sales then you don't have to do it in one query. Case will just calculate everything, including nulls(that will come up because you don't have 'else')
you might want to try subquery instead or use CTE.
CTE example
with CTE as (
Select UserID
,Agent
,COUNT(*) as Canceled
from PDS_SALES_PMI
where SaleDate >=DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-6, 0)
AND SaleDate < DATEADD(MONTH, DATEDIFF(MONTH, -6, GETDATE())-6, 0)
AND LiveAtCutOff_PC='0'
AND SaleDate >='2019-01-01'
AND Leaver = 0
group by UserID, AgentID)
Select a.UserID
,a.AgentID
,Canceled/COUNT(*) * 100.0 as Canceled_Percent
From PDS_SALES_PMI a INNER JOIN CTE
on a.AgentID = CTE.AgentID
and a.UserID = CTE.UserID
Where SaleDate >=DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-6, 0)
AND SaleDate < DATEADD(MONTH, DATEDIFF(MONTH, -6, GETDATE())-6, 0)
AND SaleDate >='2019-01-01'
AND Leaver = 0
Group by a.UserID, a.AgentID
Subquery Example
Select a.UserID
,a.AgentID
,Canceled/COUNT(*) * 100.0 as Canceled_Percent
From PDS_SALES_PMI a INNER JOIN (
Select UserID
,Agent
,COUNT(*) as Canceled
from PDS_SALES_PMI
where SaleDate >=DATEADD(MONTH, DATEDIFF(MONTH, 0,GETDATE())-6, 0)
AND SaleDate < DATEADD(MONTH, DATEDIFF(MONTH, -6, GETDATE())-6, 0)
AND LiveAtCutOff_PC='0'
AND SaleDate >='2019-01-01'
AND Leaver = 0
group by UserID, AgentID) as Tmp
on a.AgentID = Tmp.AgentID
and a.UserID = Tmp.UserID
Where SaleDate >=DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-6, 0)
AND SaleDate < DATEADD(MONTH, DATEDIFF(MONTH, -6, GETDATE())-6, 0)
AND SaleDate >='2019-01-01'
AND Leaver = 0
Group by a.UserID, a.AgentID

Use a date based on field value

I need to find a way to relate two sets of orders. Essentially, and inbound versus outbound analysis for a report. A load balance to see when I need to organize more pickups or deliveries.
I need to pull the delivery_date when billing_group = 3
I need to pull the origin_date when billing_group = 4
Hope this made some sense. Thank you all!
select convert(varchar,Delivery_Date,101) as 'Delivery_Date',
convert(varchar,Origin_Date,101) as 'Origin_Date',
sum(case when billing_group = '3' then 1 else 0 end) as 'OR to WA',
sum(case when billing_group = '4' then 1 else 0 end) as 'WA to OR',
count(*) as Total_Orders
from orders
where Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and
billing_group in ('3','4')
group by Date
order by date desc
Would it be possible to combine these two separate queries into one to provide a the following columns? date, OR to WA, and WA to OR (like in the example above) where the date (regardless of origin or delivery date is used
select convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('3')
group by delivery_date
order by date desc
select convert(varchar,Origin_Date,101) as 'Date',
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('4')
group by origin_Date
order by date desc
I think you need to group by convert(varchar,Delivery_Date,101) instead of date.
When you use aggregate function you need to add non-aggregate colunms in group by.
select convert(varchar,Delivery_Date,101) as 'Delivery_Date',
sum(case when billing_group = '3' then 1 else 0 end) as 'OR to WA',
sum(case when billing_group = '4' then 1 else 0 end) as 'WA to OR',
count(*) as Total_Orders
from orders
where Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and
billing_group in ('3','4')
group by convert(varchar,Delivery_Date,101)
order by date desc
EDIT
I saw you edit your question, you can try to use UNION ALL
SELECT * FROM (
select convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('3')
group by delivery_date
UNION ALL
select convert(varchar,Origin_Date,101) as 'Date',
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('4')
group by origin_Date
) t1
ORDER BY Date desc

Combine queries to show one set of results

I have two separate queries where I need to match up and combine together based on a date value.
select convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('3')
group by delivery_date
order by date desc
select convert(varchar,Origin_Date,101) as 'Date',
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('4')
group by origin_Date
order by date desc
Please note that I am using a different date value (delivery_date) in the first query and a different for the second (origin_date)
Thanks for the help!!
I think you just want conditional aggregation, but your query offers other room for improvement:
select convert(varchar(255), v.thedate, 101) as [Date],
sum(case when o.billing_group = 3 then 1 else 0 end) as [OR to WA],
sum(case when o.billing_group = 4 then 1 else 0 end) as [WA to OR]
from orders o cross apply
(values (case when o.billing_group = 3 then delivery_date else o.origin_date end)
) v(the_date)
where v.thedate >= dateadd(day, -30, cast(getdate() as date)) and
o.billing_group in (3, 4)
group by convert(varchar(255), v.thedate, 101)
order by v.thedate desc
Notes:
Never use varchar() without a length parameter. The length varies by context and it might not do what you expect.
I am guessing that billing_group is a number. If so, don't use single quotes. Do use single quotes if I'm wrong.
I cannot tell what the data type of delivery_date is. It is safer to aggregate by the full expression, rather than just the column.
Do not use single quotes for column aliases. Only use single quotes for string and date constants.
There is no reason to use the "trick" of adding days to 0 to remove a time component. Instead, just convert to date.
The condition on delivery_date does not have to appear in both the case expression and the where clause.
Try using UNION and adding the missing columns
then do group by and ordering
select * from (
convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA',
0 as 'WA to OR'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('3')
-- group by delivery_date
--- order by date desc
UNION
select convert(varchar,Origin_Date,101) as 'Date',
0 as 'OR to WA'
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('4')
--group by origin_Date
--order by date desc
) group by [Date]
order by [Date] desc

Count by day, count by week, in a grouped select statement

I am trying to count instances of a status by current day and current week, grouped by town.
(The table has just 3 columns: Town, status, status_date)
SELECT
MAX(dbo.Clients.Town) AS Town,
CASE
WHEN MAX(datepart(wk, status_date)) = DATEPART(wk, getdate()) THEN COUNT(Town)
ELSE 0
END AS wkTotal,
CASE
WHEN MAX(CONVERT(date, status_date, 106)) = CONVERT(date, getdate(), 106) THEN COUNT(Town)
ELSE 0
END AS dayTotal
FROM
dbo.Clients
WHERE
dbo.Clients.Status LIKE 'Status 1%'
AND MONTH(GETDATE()) = MONTH(dbo.Clients.Status_date)
AND YEAR(GETDATE())= YEAR(dbo.Clients.Status_date)
GROUP BY
dbo.Clients.Town
ORDER BY
dbo.Clients.Town
This code just returns a month count for both day total and week total columns
Hope you can help.
I surgest you do this:
SELECT dbo.Clients.Town AS Town,
count(*) AS wkTotal,
sum(CASE WHEN datepart(dayofyear, status_date) = DATEPART(dayofyear, getdate()) THEN 1 ELSE 0 END) AS dayTotal
FROM dbo.Clients
WHERE dbo.Clients.Status LIKE 'Status 1%' AND
datepart(week, GETDATE()) = datepart(week, dbo.Clients.Status_date)
AND YEAR(GETDATE())= YEAR(dbo.Clients.Status_date)
GROUP BY dbo.Clients.Town
ORDER BY dbo.Clients.Town