SQL Case statement copying an excel formula - sql

I have an excel report I am trying to convert to an SSRS report using a stored proc. In the existing excel (not written by me) has a formula as follows
=IF( [Date TCY Vacated]="",0,IF([Date TCY Tenanted]="",Summary!$B$3 - ( [Date TCY Vacated]+1), IF( ( [Date TCY Tenanted] -1)- ( [Date TCY Vacated] +1) <0,0, ( [Date TCY Tenanted] -1)- ( [Date TCY Vacated] +1) ) ))
I am trying to convert this into a sql case statement.
I'm expecting this to be an INT so I can sum the column up to get total days vacant from the property
Case When [Date TCY Vacated] Is Null Then '0'
else case when [Date TCY Tenanted] = '' then #End - ([Date TCY Vacated]+1)
else case when ([Date TCY Tenanted] - 1) - ([Date TCY Vacated] +1) < 0 then 0
else Convert(Varchar(30),[Date TCY Tenanted],120) - Convert(Varchar(30),[Date TCY Vacated],120) end end end as 'Void Days'
Tried that but not working, I'm trying to add sample data but unsure how. All fields are date fields, expected results would be 2018-02-14 - 2018-02-12 = 2 (days).

I think the reason you are having problems is that you are using standard mathematical operators to work with dates.
The below example is based on your provided samples with some tweaks to use date functions instead. It may not perfectly reflect your desired outcome but hopefully will take you most of the way there:
CREATE TABLE #a ([Date TCY Vacated] DATE, [Date TCY Tenanted] DATE)
INSERT INTO #a ([Date TCY Vacated], [Date TCY Tenanted])
VALUES ('2018-02-10', '2018-02-08')
,(NULL, '2018-02-08')
,('2018-02-10', NULL)
,('2018-02-10', '2018-02-28')
DECLARE #End DATE = GETDATE()
SELECT *,
CASE WHEN [Date TCY Vacated] IS NULL
THEN 0
ELSE CASE WHEN [Date TCY Tenanted] IS NULL
THEN DATEDIFF(d, #End, DATEADD(d, 1, [Date TCY Vacated]))
ELSE CASE WHEN DATEADD(d, -1, [Date TCY Tenanted]) < DATEADD(d, 1, [Date TCY Vacated])
THEN 0
ELSE DATEDIFF(d, DATEADD(d, -1, [Date TCY Tenanted]), DATEADD(d, 1, [Date TCY Vacated]))
END
END
END
FROM #a
In particular this code uses DATEADD and DATEDIFF instead of '+' and '-' to ensure all operations occur in days.

Not a Case statement, but a SQL version of your Excel function. It should work in SSRS
Declare #tcyv Int = 1
Declare #tcyt Int = ''
Declare #summ Int = 300
Select iif(#tcyv = '', 0,
iif(#tcyt = '', #summ - (#tcyv+1),
iif(((#tcyt-1) - (#tcyv+1)) < 0, 0,
(#tcyt-1) - (#tcyv+1)))) As x
With the CASE statement:
Select Case When #tcyv = '' Then 0
When #tcyt = '' Then #summ - (#tcyv+1)
When((#tcyt-1) - (#tcyv+1)) < 0 Then 0
Else (#tcyt-1) - (#tcyv+1) End As x
Result:
298

Related

Create view with variable declaration defined by a case expression, generating a dynamic date table based on getdate() reference

I have static date table and a working query that I use to add references fields for comparison between financial periods, I would like to save as a view however the variable declaration is not permitted.
Query is shown below, the specific issues is with declaring "#this_qtr_month" and calculating "Is QTD". The variable determines [Num of Month in QTR] for today.
declare #this_qtr_month as int = case when month(getdate()) in (1,4,7,10) then 1
when month(getdate()) in (2,5,8,11) then 2
when month(getdate()) in (3,6,9,12) then 3
end
select *
,DATEDIFF(day,[Date],getdate()) as 'Days Aged'
,DATEDIFF(ww,[Date],getdate()) as 'Weeks Aged'
,DATEDIFF(qq,[Date],getdate()) as 'QTRs Aged'
,DATEDIFF(yy,[Date],getdate()) as 'Years Aged'
,case when DATEPART(dd,[Date]) <= DATEPART(dd,getdate()) then 'Y' else 'N' end as 'Is MTD'
,case when #this_qtr_month > [Num of Month in QTR] then 'Y'
when #this_qtr_month = [Num of Month in QTR] and DATEPART(dd,[Date]) <= DATEPART(dd,getdate()) then 'Y'
else 'N'
end as 'Is QTD'
,case when getdate() >= DATEFROMPARTS(year(getdate()), [Month Num], day(date_modified_LeapYear)) then 'Y' else 'N' end as 'Is YTD'
from Date_Table_Static
You could write that without a variable:
SELECT *,
DATEDIFF(DAY, [Date], GETDATE()) AS 'Days Aged',
DATEDIFF(ww, [Date], GETDATE()) AS 'Weeks Aged',
DATEDIFF(qq, [Date], GETDATE()) AS 'QTRs Aged',
DATEDIFF(yy, [Date], GETDATE()) AS 'Years Aged',
CASE
WHEN DATEPART(dd, [Date]) <= DATEPART(dd, GETDATE()) THEN
'Y'
ELSE
'N'
END AS 'Is MTD',
CASE
WHEN (CASE
WHEN MONTH(GETDATE()) IN ( 1, 4, 7, 10 ) THEN
1
WHEN MONTH(GETDATE()) IN ( 2, 5, 8, 11 ) THEN
2
WHEN MONTH(GETDATE()) IN ( 3, 6, 9, 12 ) THEN
3
END
) > [Num of Month in QTR] THEN
'Y'
WHEN (CASE
WHEN MONTH(GETDATE()) IN ( 1, 4, 7, 10 ) THEN
1
WHEN MONTH(GETDATE()) IN ( 2, 5, 8, 11 ) THEN
2
WHEN MONTH(GETDATE()) IN ( 3, 6, 9, 12 ) THEN
3
END
) = [Num of Month in QTR]
AND DATEPART(dd, [Date]) <= DATEPART(dd, GETDATE()) THEN
'Y'
ELSE
'N'
END AS 'Is QTD',
CASE
WHEN GETDATE() >= DATEFROMPARTS(YEAR(GETDATE()), [Month Num], DAY(date_modified_LeapYear)) THEN
'Y'
ELSE
'N'
END AS 'Is YTD'
FROM Date_Table_Static;
Or, since all you need with this view is a SELECT, you could write that as a Stored Procedure or TVF (Table Valued Function).
EDIT: I didn't read your expression before, it doesn't need to be that complex:
SELECT *,
DATEDIFF(DAY, [Date], GETDATE()) AS 'Days Aged',
DATEDIFF(ww, [Date], GETDATE()) AS 'Weeks Aged',
DATEDIFF(qq, [Date], GETDATE()) AS 'QTRs Aged',
DATEDIFF(yy, [Date], GETDATE()) AS 'Years Aged',
CASE
WHEN DATEPART(dd, [Date]) <= DATEPART(dd, GETDATE()) THEN
'Y'
ELSE
'N'
END AS 'Is MTD',
CASE
WHEN (Month(Getdate())-1)%3+1 > [Num of Month in QTR] THEN
'Y'
WHEN (Month(Getdate())-1)%3+1 = [Num of Month in QTR]
AND DATEPART(dd, [Date]) <= DATEPART(dd, GETDATE()) THEN
'Y'
ELSE
'N'
END AS 'Is QTD',
CASE
WHEN GETDATE() >= DATEFROMPARTS(YEAR(GETDATE()), [Month Num], DAY(date_modified_LeapYear)) THEN
'Y'
ELSE
'N'
END AS 'Is YTD'
FROM Date_Table_Static;

SQL Error with CASE WHEN Error on From when using GETDATE

I have a query that is giving me an error. The error is below. Please help.
The Created Date field is NVARCHAR type. Everything in the dbo.UNIQUE_PARTS_LIST table is NVARCHAR
14 The data types datetime and date are incompatible in the subtract operator.
DECLARE #LookBack60 as FLOAT
Set #LookBack60= -60
select distinct part.Part_Num, part.[Part Description], part.[Part Type],
CASE
WHEN
(cast(Part.[Created Date] as date) <= DATEADD(MONTH,#LookBack60,GETDATE()))
THEN 'Not Needed'
ELSE
(getdate() - cast(Part.[Created Date] as date))
END as 'Intermediate',
CASE
WHEN
(cast(Part.[Created Date] as date) <= DATEADD(MONTH,#LookBack60,GETDATE()))
THEN 'Not Needed'
ELSE
(getdate() - cast(Part.[Created Date] as date)) / (365/12)
END as 'FINAL',
from dbo.UNIQUE_PARTS_LIST part
WHERE part.[Part Type] = 'Consumable'
order by part.Part_Num asc
This is solved. I changed it to DATEDIFF.

Create a value adding two columns together

I am attempting to define and new column called End Year that is the calculation of another column plus a number representing number of years. For some reason my script does not recognize the new column called Allocation Year using the excerpt below SQL statement in SQL Server 2008. Note that Contract Year is an existing column that is identified in the non-redacted full script:
,[Allocation Type] =
CASE
WHEN left([contract type] ,1)'1' = THEN 'O&M'
ELSE 'N/A'
END
,[Allocation Year] =
CASE
WHEN [Contract Year]='XXXX' THEN '0'
ELSE CAST ([Contract Year] AS INT)
END
,[End Year] =
CASE
WHEN [Allocation Type]='O&M' THEN [Allocation Year] + 6
END
Columns are named with the AS keyword (which can also be elided in many cases), not with the = operator.
SQL Server does not let you reference other columns or aliases in a SELECT clause - you have to use a subquery:
Like so:
SELECT
*,
CASE WHEN [Allocation Type] = 'O&M'
THEN [Allocation Year Temp] + 6
ELSE NULL
END AS [Allocation Year]
FROM
(
SELECT
CASE WHEN LEFT( [Contract type], 1 ) = '1'
THEN 'O&M'
ELSE 'N/A'
END AS [Allocation Type],
CASE WHEN [Contract Year] = 'XXXX'
THEN '0'
ELSE CAST( [Contract Year] AS int )
END AS [Allocation Year Temp],
*
FROM
....
)
If you want to put everything in one select query, then try this one. Hope this helps. Thanks.
,[End Year] =
CASE
WHEN left([contract type] ,1)='1' THEN
(CASE
WHEN [Contract Year]='XXXX' THEN 6
ELSE (CAST ([Contract Year] AS INT) + 6 )
END)
END
You cannot reference computed columns in the SELECT statement. You'll have to repeat the calculation (or use subqueries like Dai suggested):
,[Allocation Type] =
CASE
WHEN left([contract type] ,1)='1' THEN 'O&M'
ELSE 'N/A'
END
,[Allocation Year] =
CASE
WHEN [Contract Year]='XXXX' THEN 0
ELSE CAST ([Contract Year] AS INT)
END
,[End Year] =
CASE
WHEN left([contract type] ,1)='1' THEN (CASE
WHEN [Contract Year]='XXXX' THEN 0
ELSE CAST ([Contract Year] AS INT)
END) + 6
END

SQL Date Logic Clause

Having problems understanding how to get the Where clause to work with this date structure.
Here is the principal logic. I want data only from previous March 1 onward and ending on yesterdays date.
Example #1:
So today is Feb 13, 2015 This would mean I need data between (2014-03-01 and 2015-02-12)
Example #2:
Say today is March 20, 2015 This This would mean I need data between (2015-03-01 and 2015-03-19)
The where logic might work but it doesn't like to convert '3/1/' + year. But I'm not sure how else to express it. The first clause is fine its the Case section that is broken.
Query
SELECT [Request Date], [myItem]
FROM myTable
WHERE [Request Date] < CONVERT(VARCHAR(10), GETDATE(), 102)
AND [Request Date] = CASE WHEN
CONVERT(VARCHAR(10), GETDATE(), 102) <
CONVERT(VARCHAR(12), '3/1/' + DATEPART ( year , GETDATE()) , 114)
THEN [Request Date] > CONVERT(VARCHAR(12), '3/1/' + DATEPART ( year , GETDATE()-365) , 114)
ELSE [Request Date] > CONVERT(VARCHAR(12), '3/1/' + DATEPART ( year , GETDATE() , 114)
END
I have also tried
AND [Request Date] = CASE WHEN
CONVERT(VARCHAR(10), GETDATE(), 102) <
'3/1/' + CONVERT(VARCHAR(12), DATEPART ( YYYY , GETDATE()))
THEN [Request Date] > '3/1/' + CONVERT(VARCHAR(12), DATEPART ( YYYY , GETDATE()-364))
ELSE [Request Date] > '3/1/' + CONVERT(VARCHAR(12), DATEPART ( YYYY , GETDATE()))
END
Try this where clause.
WHERE [Request Date]
BETWEEN Cast(CONVERT(VARCHAR(4), Year(Getdate())-1)+ '-03-01' AS DATE)
AND Getdate() - 1
Here Cast(CONVERT(VARCHAR(4), Year(Getdate())-1)+ '-03-01' AS DATE) will fetch the first day of march month. With that add -1 year to get the starting point.
Getdate() - 1 will define the ending point
I'd prefer to create datetime variables for the #from - #to range but if this is for a view I guess you have to do it in the where clause.
SELECT [Request Date], [myItem]
FROM myTable
WHERE [Request Date] < cast(GETDATE() as date)
AND [Request Date] >= CASE WHEN
GETDATE() < CONVERT(datetime, '3/1/' + cast(Year(GETDATE()) as varchar(4)))
THEN CONVERT(datetime, '3/1/' + cast(Year(GETDATE()) - 1 as varchar(4)))
ELSE CONVERT(datetime, '3/1/' + cast(Year(GETDATE()) as varchar(4)))
END
Something like this? Always from Mar 1st onwards, previous year if it's now Mar 1 or earlier, and otherwise this year.
SELECT [Request Date], [myItem]
FROM myTable
WHERE [Request Date] >= dateadd(month, 2, DATEADD(year, DATEDIFF(year, 0, dateadd(month, -2, dateadd(day, -1, getdate()))), 0))
and [Request Date] < DATEADD(day, DATEDIFF(day, 0, getdate()), 0)
First it deducts one day, so that March 1 isn't taking the same year, then it deducts 2 months for getting those dates for previous year, then it rounds it to the year, and then it adds 2 months to get to Mar 1.
In Oracle, I'd compute the lower bound like this:
add_months( trunc( add_months( sysdate, -2 ), 'YEAR'), 2 )
In other words - subtract two months, round down to the start of the year, then add two months.
Hopefully, you can convert this to use appropriate TSQL functions.
Lets work with some test data:
DECLARE #MyDate DATETIME = '3/13/2015'
Going to declare some variable we will set:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
In this code, I check to see if we are before or after march 1st, and if so we will use either the previous year, or this year for the starting point (fiscal year?)
SELECT #StartDate = CASE WHEN DATEPART(MONTH, #MyDate) < 3 THEN
DATEADD(MONTH, 2, DATEADD(YEAR, DATEDIFF(YEAR, 0, #MyDate) - 1, 0))
ELSE
DATEADD(MONTH, 2, DATEADD(YEAR, DATEDIFF(YEAR, 0, #MyDate), 0))
END,
#EndDate = DATEADD(DAY, DATEDIFF(DAY, 0, #MyDate), 0)
Here is the output:
SELECT #StartDate AS Start, #EndDate AS EndDate
Start EndDate
2015-03-01 00:00:00.000 2015-03-13 00:00:00.000
I would first create a fairly generic user-defined function that does what I need, thus:
create function dbo.start_of_fiscal_year
(
#today date ,
#fiscal_year_start_month int
)
returns date
as
begin
set #today = case coalesce(#today,'')
when '' then current_timestamp
else #today
end
declare #month_start date = dateadd(day,1-datepart(day,#today),#today)
declare #fiscal_month_number int = case sign( datepart(month,#month_start) - #fiscal_year_start_month )
when -1 then 13
else 1
end
+ ( datepart(month,#month_start) - #fiscal_year_start_month )
declare #fiscal_year_start date = dateadd(month,1-#fiscal_month_number,#month_start)
return #fiscal_year_start
end
go
Once you have that you can say things like
declare #today date = current_timetamp
declare #fy_start date = start_of_fiscal_year(#today,3)
select *
from dbo.foo t
where t.report_date >= #fy_start
and t.report_date < #today
or even
select fiscal_year = datepart(year,start_of_fiscal_year(t.report_date,3)) , count(*)
from dbo.foo t
group by datepart(year,start_of_fiscal_year(t.report_date,3))
Your lower bound should be this. You just need to offset the year when month is less than 3 (March).
dateadd(
yy,
year(current_timestamp) - 1900 + case when month(current_timestamp) < 3 then -1 else 0 end,
'19000301'
)
There's no reason to mess around with strings and this consolidates the logic very concisely. I'm also guessing that when current date is March 1 that you want to query the full previous year. So you'll want to adjust the test slightly.
case when (month(dateadd(dd, -1, current_timestamp)) < 3 ...
And just for fun:
dateadd(mm, (12-month(current_timestamp-1))/10*-12+2, cast(year(current_timestamp) as char(4)));

CTE in subQuery

I have a scenario to display data from 2 tables combined. First table Named 'DayTable' consists of daily plan and actual. Second table named 'MonthTable' consists of Monthly plan and actual. I need to display last 6months data and the current month daily data. So i wrote the query like below i for the expected output
Declare #startdate date = CONVERT(DATE, DATEADD(dd, -DAY(DATEADD(MONTH, 0, GETDATE())) + 1, DATEADD(MONTH, 0, GETDATE())))
Declare #endDate date = DATEADD(DAY, -DAY(DATEADD(MONTH, 1, GETDATE())), DATEADD(MONTH, 1, GETDATE()))
CREATE TABLE #TEMP
(
PlanDate NVARCHAR(100),
[PastTrend - Plan] INT,
[PastTrend - Actual] INT,
[Current - Plan] INT,
[Current - Actual] INT,
)
;With cte
as
(
Select #startdate sDate
Union All
Select DATEADD(day,1,sDate) From cte where DATEADD(day,1,sDate) <= #endDate
)
INSERT INTO #TEMP
SELECT
REPLACE(CONVERT(CHAR(6), A.sDate, 106),' ',' - ') PlanDate
,NULL AS [PastTrend - Plan]
,NULL AS [PastTrend - Actual]
,SUM(B.PlanQuantity) AS [Current - Plan]
,SUM(B.Actual) AS [Current - Actual]
FROM cte A
LEFT OUTER JOIN DayTable B
ON A.sDate = CONVERT(DATE,B.PlanDate)
GROUP BY A.sDate
--ORDER BY A.sDate
SELECT
*
FROM
(
SELECT
CONVERT(CHAR(3), datename(month,PlanMonth)) + ' ' + RIGHT(CONVERT(VARCHAR(4), YEAR(PlanMonth)), 2) AS PlanDate
,SUM(PlanQuantity) AS [PastTrend - Plan]
,SUM(Actual) AS [PastTrend - Actual]
,NULL AS [Current - Plan]
,NULL AS [Current - Actual]
FROM
MonthTable
WHERE CONVERT(DATE, PlanMonth) >= CONVERT(DATE, DATEADD(dd, -DAY(DATEADD(MONTH, 0, GETDATE())) + 1, DATEADD(MONTH, -6, GETDATE())))
group by PlanMonth
UNION ALL
SELECT
PlanDate
,[PastTrend - Plan]
,[PastTrend - Actual]
,[Current - Plan]
,[Current - Actual]
FROM
#TEMP
) T1
DROP TABLE #TEMP
My Output is like
Now i am thining to avoid temp table concept because if any failure after create temp table it will not drop. So rewrite the query like below
Declare #startdate date = CONVERT(DATE, DATEADD(dd, -DAY(DATEADD(MONTH, 0, GETDATE())) + 1, DATEADD(MONTH, 0, GETDATE())))
Declare #endDate date = DATEADD(DAY, -DAY(DATEADD(MONTH, 1, GETDATE())), DATEADD(MONTH, 1, GETDATE()))
;With cte
as
(
Select #startdate sDate
Union All
Select DATEADD(day,1,sDate) From cte where DATEADD(day,1,sDate) <= #endDate
)
SELECT
A.sDate AS OriginalDate
,REPLACE(CONVERT(CHAR(6), A.sDate, 106),' ',' - ') PlanDate
,NULL AS [PastTrend - Plan]
,NULL AS [PastTrend - Actual]
,SUM(B.PlanQuantity) AS [Current - Plan]
,SUM(B.Actual) AS [Current - Actual]
FROM cte A
LEFT OUTER JOIN DayTable B
ON A.sDate = CONVERT(DATE,B.PlanDate)
GROUP BY A.sDate
UNION ALL
SELECT
PlanMonth AS OriginalDate
,CONVERT(CHAR(3), datename(month,PlanMonth)) + ' ' + RIGHT(CONVERT(VARCHAR(4), YEAR(PlanMonth)), 2) AS PlanDate
,SUM(PlanQuantity) AS [PastTrend - Plan]
,SUM(Actual) AS [PastTrend - Actual]
,NULL AS [Current - Plan]
,NULL AS [Current - Actual]
FROM
MonthTable
WHERE CONVERT(DATE, PlanMonth) >= CONVERT(DATE, DATEADD(dd, -DAY(DATEADD(MONTH, 0, GETDATE())) + 1, DATEADD(MONTH, -6, GETDATE())))
group by PlanMonth
ORDER BY OriginalDate
But here i have problem. In the output i dont need OriginalDate. How to avoid this. For this i can wrap the union output to a select query but how can i got error in cte. Please guid me. Also suggest which method is best one
I finished the query. ACtually i just wrap the query as outer with out the cte. Cte must be the top in the query. The final query is
Declare #startdate date = CONVERT(DATE, DATEADD(dd, -DAY(DATEADD(MONTH, 0, GETDATE())) + 1, DATEADD(MONTH, 0, GETDATE())))
Declare #endDate date = DATEADD(DAY, -DAY(DATEADD(MONTH, 1, GETDATE())), DATEADD(MONTH, 1, GETDATE()))
;With cte
as
(
Select #startdate sDate
Union All
Select DATEADD(day,1,sDate) From cte where DATEADD(day,1,sDate) <= #endDate
)
SELECT
T1.PlanDate
,T1.[PastTrend - Plan]
,T1.[PastTrend - Actual]
,T1.[Current - Plan]
,T1.[Current - Actual]
FROM
(
SELECT
A.sDate AS OriginalDate
,REPLACE(CONVERT(CHAR(6), A.sDate, 106),' ',' - ') PlanDate
,NULL AS [PastTrend - Plan]
,NULL AS [PastTrend - Actual]
,SUM(B.PlanQuantity) AS [Current - Plan]
,SUM(B.Actual) AS [Current - Actual]
FROM cte A
LEFT OUTER JOIN DayTable B
ON A.sDate = CONVERT(DATE,B.PlanDate)
GROUP BY A.sDate
UNION ALL
SELECT
PlanMonth AS OriginalDate
,CONVERT(CHAR(3), datename(month,PlanMonth)) + ' ' + RIGHT(CONVERT(VARCHAR(4), YEAR(PlanMonth)), 2) AS PlanDate
,SUM(PlanQuantity) AS [PastTrend - Plan]
,SUM(Actual) AS [PastTrend - Actual]
,NULL AS [Current - Plan]
,NULL AS [Current - Actual]
FROM
MonthTable
WHERE CONVERT(DATE, PlanMonth) >= CONVERT(DATE, DATEADD(dd, -DAY(DATEADD(MONTH, 0, GETDATE())) + 1, DATEADD(MONTH, -6, GETDATE())))
group by PlanMonth
) T1
ORDER BY T1.OriginalDate
But i need to know the peroformance. When i execute this query with actual execution plan Query Cost (Relative to the batch) : 100%
When i execute the first method using temp the query cost is 90%. Can anyone guid for this