Divide by Case When - sql

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

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

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

Remove old records based on date SQL

Here is my query
SELECT
cf.CLIENTID, p.Id as ProfileID, t.TITLEDESC as Title,
cf.ClntForenme as Name, cf.CLNTSURNME as Surname,
pm.Lender, pm.Product, pm.LenderReference,
pm.AmountRequested as LoanAmount,
pm.DateCompleted,
CASE
WHEN pm.DateCompleted BETWEEN (SELECT DATEADD(YEAR, -1, GETDATE())) AND GETDATE()
THEN 'Completed under a year ago'
WHEN pm.DateCompleted BETWEEN (SELECT DATEADD(YEAR, -2, GETDATE())) AND (SELECT DATEADD(YEAR, -1, GETDATE()))
THEN 'Backlog WOM 1 Year'
WHEN pm.DateCompleted BETWEEN (SELECT DATEADD(YEAR, -3, GETDATE())) AND (SELECT DATEADD(YEAR, -2, GETDATE()))
THEN 'Backlog WOM 2 Year'
WHEN pm.DateCompleted BETWEEN (SELECT DATEADD(YEAR, -4, GETDATE())) AND (SELECT DATEADD(YEAR, -3, GETDATE()))
THEN 'Backlog WOM 3 Year'
ELSE ''
END Source,
CASE
WHEN pm.Id > 0 THEN 'Check Perspectiove for ERC'
ELSE ''
END ERC,
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ID
FROM
tbl_Profile as p
INNER JOIN
tbl_Profile_Mortgage as pm ON p.id = pm.FK_ProfileId
LEFT JOIN
tbl_ClientFile as cf ON p.ClientId = cf.CLIENTID
LEFT JOIN
[dbo].tbl_DDTitles as t ON cf.CLNTTITLE = t.titleid
WHERE
pm.MortgageStatus = 7
AND p.CaseTypeId = 1
AND pm.DateCompleted BETWEEN (SELECT DATEADD(YEAR, -4, GETDATE())) AND GETDATE()
This query returns 990 records.
Some clients will have multiple profiles (ProfileID) what I want to do is only show me the ProfileID with the latest DateCompleted.
So, for instance, I have a clientID of 5566 this client has 3 profiles each with there own Datecompleted i only want to see the last profiles information based on Datecompleted.
Any help would be much appreciated.
Cheers
One option for you is to partition your data by client, profile, then check which datecompleted is the greatest value (aka whichever ones have a DateRank of 1). Then just select those that have that value as 1.
select * from
(
SELECT cf.CLIENTID ,p.Id as ProfileID,t.TITLEDESC as Title,cf.ClntForenme as Name,cf.CLNTSURNME as Surname,pm.Lender,pm.Product,pm.LenderReference,pm.AmountRequested as LoanAmount,
pm.DateCompleted,
CASE
WHEN pm.DateCompleted BETWEEN (select dateadd(year, -1, getdate())) AND getdate() THEN 'Compelted Under a year ago'
WHEN pm.DateCompleted BETWEEN (select dateadd(year, -2, getdate())) AND (select dateadd(year, -1, getdate())) THEN 'Backlog WOM 1 Year'
WHEN pm.DateCompleted BETWEEN (select dateadd(year, -3, getdate())) AND (select dateadd(year, -2, getdate())) THEN 'Backlog WOM 2 Year'
WHEN pm.DateCompleted BETWEEN (select dateadd(year, -4, getdate())) AND (select dateadd(year, -3, getdate())) THEN 'Backlog WOM 3 Year'
ELSE ''
END Source,
CASE
WHEN pm.Id > 0 THEN 'Check Perspectiove for ERC'
ELSE ''
END ERC,
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ID,
ROW_NUMBER() OVER (Partition by CLIENTID, ProfileID order by DateCompleted desc) as DateRank --Changes here
FROM tbl_Profile as p
INNER JOIN tbl_Profile_Mortgage as pm
ON p.id = pm.FK_ProfileId
LEFT JOIN tbl_ClientFile as cf
ON p.ClientId = cf.CLIENTID
LEFT JOIN [dbo].tbl_DDTitles as t on cf.CLNTTITLE = t.titleid
WHERE pm.MortgageStatus = 7 and p.CaseTypeId = 1
AND pm.DateCompleted between (select dateadd(year, -4, getdate())) AND getdate()
) clientinfo
where clientinfo.DateRank = 1

How to check a SQL CASE with multiple conditions?

I have two tables. Accounts ACC and FinancialTrans FT
The FinancialTrans table is as follows:
AcctID TransTypeCode DateOfTrans
123 TOLL 2016-06-06 00:00:00.000
123 TOLL 2016-06-02 00:00:00.000
123 TOLL 2016-04-28 00:00:00.000
123 PYMT 2016-03-11 00:00:00.000
123 TOLL 2015-12-22 00:00:00.000
123 TOLL 2015-12-22 00:00:00.000
The requirement is:
When any Accounts have NO 'TOLL' or 'PYMT' in the last 2 years print 'Flag'
SELECT ACC.Field1
,ACC.Field2
,ACC.Field3
,ACC.Field4
,CASE WHEN
(SELECT Max(DateOfTrans) FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND (TransTypeCode = 'TOLL' AND DateOfTrans >= DATEADD(year, -2, GETDATE()))
AND (TransTypeCode = 'PYMT' AND DateOfTrans >= DATEADD(year, -2, GETDATE()))
GROUP BY AcctID, TransTypeCode) IS NULL
THEN 'Flag'
ELSE ''
AND AS NoNo_Flag
FROM Accounts ACC
WHERE Condition 1, Condition 2...
try this one:
SELECT
acc.*,
CASE WHEN f.acctid IS NULL THEN 'flag' ELSE '' END AS flag_noTollOrPmt
FROM
accounts acc LEFT OUTER JOIN
(SELECT
AcctID,
MAX(DateOfTrans) AS max_dateOfTrans_TollOrPmt
FROM
FinanceTrans
WHERE
DateOfTrans >= DATEADD(YEAR, -2, GETDATE()) AND
TransTypeCode IN( 'TOLL' , 'PYMT')
GROUP BY
AcctID) f ON
acc.acctid = f.acctid
You should be using window functions. The logic is to look at the maximum date for the two transaction types. The flag then depends on the relationship to the current date.
select a.*,
(case when max(case when transtype in ('TOLL', 'PYMT') then DateOfTrans end) over
(partition by acctid) >= dateadd(year, -2, getdate())
then 0 else 1
end) as flag
from accounts;
I could be misunderstanding the question, in which case I can refine my answer. It seems like you just need to check for the existence, or not, of records in the the sub-query. So to that extent do you really need to do an aggregate? And, try using EXISTS:
SELECT ACC.Field1, ACC.Field2, ACC.Field3, ACC.Field4,
CASE WHEN NOT EXISTS
(SELECT DateOfTrans
FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND (TransTypeCode = 'TOLL' AND DateOfTrans >= DATEADD(year, -2, GETDATE()))
AND (TransTypeCode = 'PYMT' AND DateOfTrans >= DATEADD(year, -2, GETDATE())))
THEN 'Flag'
ELSE ''
END AS NoNo_Flag
FROM Accounts ACC
WHERE [*condition*]
So this is how I resolved this issue:
I created a separate column for each, first and then stored those details in a temporary table.
Then I pulled data from the temporary table using conditions to create the flag.
My code is as follows:
SELECT ACC.Field1
,ACC.Field2
,ACC.Field3
,ACC.Field4
,(SELECT Max(DateOfTrans) FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND TransTypeCode = 'TOLL'
GROUP BY AcctID, TransTypeCode) LastTollDate
,(SELECT Max(DateOfTrans) FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND TransTypeCode = 'PYMT'
GROUP BY AcctID, TransTypeCode) LastPymtDate
INTO #Temp_Data
FROM Accounts ACC
WHERE Condition 1, Condition 2...
SELECT ACC.Field1
,ACC.Field2
,ACC.Field3
,ACC.Field4
,CASE WHEN LastTollDate >= DATEADD(year, -2, GETDATE())
AND LastPymtDate >= DATEADD(year, -2, GETDATE())
THEN 'Flag'
ELSE ''
END AS Flag
FROM #Temp_Data

SQL Group by month and join

I have 3 different queries and need to find a way to display results in a single gridview, but this way is creating 3 different grids.
My code is this and try to find a way to join in a sinlge one with rows showing months and columns showing "Laptop", "Pc" and "Total".
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0) AS MyMonth, COUNT(stb_group) AS Laptop
FROM inventary
WHERE (activation_date > '10-01-2013') AND (stb_group = 'Laptop')
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0)
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0) AS MyMonth, COUNT(stb_group) AS PC
FROM inventary
WHERE (activation_date > '10-01-2013') AND (stb_group = 'PC')
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0)
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0) AS MyMonth, COUNT(stb_group) AS Units
FROM inventary
WHERE (activation_date > '10-01-2013')
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0)
Try this
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0) AS MyMonth,
COUNT(case when stb_group = 'Laptop' then stb_group end) AS Laptop,
COUNT(case when stb_group = 'PC' then stb_group end) AS PC,
COUNT(stb_group) AS Units
FROM inventary
WHERE (activation_date > '10-01-2013')
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, activation_date), 0)