T-SQL CASE Statement to determine what is compared - sql-server

T-SQL 2018 coding/logic question. I have CASE in a SELECT statement. Below I have provided the "pseudo code" of what the requirement is, but I need assistance in how to write the last AND of the CASE statement. Is it possible to change which condition is used inside a CASE statement based on values from other fields?
SELECT
[DocumentNo],
[DocumentType],
CASE
WHEN [DocumentStatus] IS 'APPROVED'
AND [DocumentBusiness] = 'COMMERCIAL'
AND DATEDIFF(Hours, [ReceivedDate], GETDATE()) < 5 but if
[ReceivedDate] is null, then use DateDiff(Hours,[ProcessDate],
GETDATE()) < 10 instead.
THEN 1
ELSE 0
END AS 'DocumentPerformance'
FROM
DocumentTbl01

Maybe something like this? Just add a second WHEN and include that condition IF ReceivedDate IS (NOT) NULL into the WHEN conditions for both - once with IS NOT NULL, once with IS NULL.
SELECT
[DocumentNo],
[DocumentType],
CASE
WHEN [DocumentStatus] = 'APPROVED'
AND [DocumentBusiness] = 'COMMERCIAL'
AND ReceivedDate IS NOT NULL
AND DATEDIFF(Hours, [ReceivedDate], GETDATE()) < 5
THEN 1
WHEN [DocumentStatus] = 'APPROVED'
AND [DocumentBusiness] = 'COMMERCIAL'
AND ReceivedDate IS NULL
AND DATEDIFF(Hours, [ProcessDate], GETDATE()) < 10
THEN 1
ELSE 0
END AS 'DocumentPerformance'
FROM
DocumentTbl01

It can be done this way:
SELECT
[DocumentNo],
[DocumentType],
CASE
WHEN [DocumentStatus] = 'APPROVED'
AND [DocumentBusiness] = 'COMMERCIAL'
AND (
(ReceivedDate IS NOT NULL AND DATEDIFF(Hours, [ReceivedDate], GETDATE()) < 5)
OR
(ReceivedDate IS NULL AND DATEDIFF(Hours, [ProcessDate], GETDATE()) < 10)
)
THEN 1
ELSE 0
END AS 'DocumentPerformance'
FROM DocumentTbl01
Or to simplify even more, you can do the below which does NULL checking first.
SELECT
[DocumentNo],
[DocumentType],
IIF
(
[DocumentStatus] = 'APPROVED'
AND [DocumentBusiness] = 'COMMERCIAL'
AND
(
(ReceivedDate IS NULL AND DATEDIFF(Hours, [ProcessDate], GETDATE()) < 10)
OR (DATEDIFF(Hours, [ReceivedDate], GETDATE()) < 5)
)
,1
,0
) AS 'DocumentPerformance'
FROM DocumentTbl01

Seems like you could just use ISNULL
SELECT
DocumentNo,
DocumentType,
CASE
WHEN DocumentStatus = 'APPROVED'
AND DocumentBusiness = 'COMMERCIAL'
AND DATEDIFF(hour, ISNULL(ReceivedDate, ProcessDate), GETDATE()) < 10
THEN 1
ELSE 0
END AS DocumentPerformance
FROM
DocumentTbl01;

Related

SQL SUM BETWEEN TWO COUNT RESULT

SELECT
[Soldtopt],
[tradingname],
[DlvDate],
SUM(try_cast(Netvalue as float)) as Netvalue,
count(distinct SDDoc) as Salesdoc ,
count(distinct case when Netvalue = '0' then 1 else null end) as ZeroValue ,
sum (count(distinct SDDoc)) , (count(distinct case when Netvalue = '0' then 1 else null end)) As result
FROM [FOC].[dbo].[foc]
GROUP by Soldtopt,tradingname,DlvDate ORDER BY count (distinct SDDoc) DESC;
is this correct to way to bring the sum(
sum (count(distinct SDDoc)) , (count(distinct case when Netvalue = '0' then 1 else null end)) As result)
? or i am getting error "
Msg 130, Level 15, State 1, Line 13
Cannot perform an aggregate function on an expression containing an aggregate or a subquery."
[enter image description here][1]
[1]: https://i.stack.imgur.com/u1LGz.jpg hi in this result should be like 2,2,0,1 where as result shows like 35,35,35,35
Use SUM OVER()
SELECT [Soldtopt],
[tradingname],
[DlvDate],
Sum(try_cast(Netvalue as float)) AS Netvalue,
Count(DISTINCT SDDoc) AS Salesdoc,
Count(DISTINCT CASE WHEN Netvalue = '0' THEN 1 ELSE NULL END) AS ZeroValue, -- will always return 1
Sum (Count(DISTINCT SDDoc))OVER(),
Count(DISTINCT CASE WHEN Netvalue = '0' THEN 1 ELSE NULL END) AS result -- will always return 1
FROM [FOC].[dbo].[foc]
GROUP BY Soldtopt,
tradingname,
DlvDate
ORDER BY Count (DISTINCT SDDoc) DESC;
Also this Count aggregate will always return 1
Count(DISTINCT CASE WHEN Netvalue = '0' THEN 1 ELSE NULL END)
Adding sample data and expected result can fix the logical mistakes in your query
ORDER BY count (distinct SDDoc) DESC;
This will sort the data according to the number of col whose index is the value returned by the count function.
sum (count(distinct SDDoc))
This isn't a valid statement.
count(distinct case when Netvalue = '0' then 1 else null end)
Also try using a subquery in this count.
solved.count(distinct SDDoc) - count(distinct case when Netvalue = '0' then 1 else null end) As Result

Operand data type varchar is invalid for sum operator using a sum function with an imbedded datediff in a case statement

I am trying to sum the total number of rows that have a '2' as the difference between col1 and col2. But when I use the below query, I get the "operand data type varchar is invalid for sum operator".
I am not sure if this matters but here are some of the facts;
col1 data type is datetime
col2 data type is datetime
col3 data type is datetime
Query:
SELECT
SUM(CASE WHEN datediff(day,_col1,_col2) = '0' THEN '1' ELSE '0' END) day0
FROM dbo1
WHERE year(_col3) between year(getdate())-'1' and year(getdate())
AND _col1 is not null
AND _col2 is not null
SELECT SUM(CASE WHEN datediff(day,_col1,_col2) = 0 THEN 1 ELSE 0 END) day0
FROM dbo1
WHERE _col3 >= dateadd(year, datediff(year, 0, getdate()) - 1, 0)
and _col3 < dateadd(year, datediff(year, 0, getdate()) + 1, 0)
AND _col1 is not null
AND _col2 is not null
You use chars as a argument for the sum method.
why u didn't use the NUMBER!?
try this:
SELECT
SUM(CASE WHEN datediff(day,_col1,_col2) = 0 THEN 1 ELSE 0 END) day0
FROM dbo1
WHERE year(_col3) between year(getdate())-1 and year(getdate())
AND _col1 is not null
AND _col2 is not null

How to do conditional select using SQL Server 2005

I have following query that I want to improve so it displays two additional columns as Variance and Percentage_Variance.
declare #w0s datetime
declare #w0e datetime
declare #w1s datetime
declare #w1e datetime
set #w0s = dateadd(hh, datediff(hh, 0, getdate()), 0)
set #w0e = getdate()
set #w1s = dateadd(dd, datediff(dd, 0, getdate()) / 7 * 7 - 7, 0)
set #w1e = dateadd(dd, datediff(dd, 0, getdate()) / 7 * 7, 0)
select
state,
sum(case
when create_time >= #w0s and create_time < #w0e
then 1 else 0
end) as Call_Volume_This_hr,
sum(case
when create_time >= #w1s and create_time < #w1e
then 1 else 0
end) / 7.0 as Avg_Call_Volume_Past_Week1
--,(case when ((sum(case when create_time>=#w0s and create_time<#w0e then 1 else 0 end)) - (sum(case when create_time>=#w1s and create_time<#w1e then 1 else 0 end)/7.0) > 0)then 1 else 0 end) as Variance
--,(case when (((case when ((sum(case when create_time>=#w0s and create_time<#w0e then 1 else 0 end)) - (sum(case when create_time>=#w1s and create_time<#w1e then 1 else 0 end)/7.0) > 0)then 1 else 0 end)/(sum(case when create_time>=#w1s and create_time<#w1e then 1 else 0 end)/7.0)*100.0) > 0) then 1 else 0 end) as Percentage_Variance
from
ctirpts.dbo.cti_reporting
where
create_time >= #w1s
and datepart(hh, create_time) = datepart(hour, getdate())
and state is not null
group by
state
;
The condition is that
if (Call_Volume_This_hr > Avg_Call_Volume_Past_Week1)
then
Variance = Call_Volume_This_hr - Avg_Call_Volume_Past_Week1
and
Percentage_Variance = (Variance/Avg_Call_Volume_Past_Week1)*100
else
Variance = 0 and Percentage_Variance = 0
The two commented lines shows my feeble attempt at getting this done but it prints 1 under columns "Variance" and "Percentage_Variance" when "if (Call_Volume_This_hr > Avg_Call_Volume_Past_Week1)" is true. Instead I want the actual computed values as per:
Variance = Call_Volume_This_hr - Avg_Call_Volume_Past_Week1
Percentage_Variance = (Variance/Avg_Call_Volume_Past_Week1)*100
I would appreciate any help or pointers.
You could put your initial select into a common table expression, then those calculated SUM columns become much more readily available for conditional computation. You can use an APPLY operator to perform the CASE statement, returning Variance of zero or more (never negatives). Then you can perform the calculation to get the percentage variance.
-- this CTE performs the initial aggregation and makes the recordset available
-- to be used like a table:
;with PreAggregate
as (
select state
,sum(case when create_time>=#w0s and create_time<#w0e then 1 else 0 end) as Call_Volume_This_hr
,sum(case when create_time>=#w1s and create_time<#w1e then 1 else 0 end)/7.0 as Avg_Call_Volume_Past_Week1
from ctirpts.dbo.cti_reporting
where create_time>=#w1s
and datepart(hh,create_time)=datepart(hour, getdate())
and state is not null
group by state
)
-- The APPLY operator creates an inline-table-value function without the
-- "black box" query optimizer problems of using an actual UDF
-- The "CROSS APPLY" calculates a 0 or more value for Variance.
-- The Variance value can then be used to compute Percentage_Variance.
select PreAggregate.state
, PreAggregate.Call_Volume_This_hr
, PreAggregate.Avg_Call_Volume_Past_Week1
, InlineFunction.Variance
, (InlineFunction.Variance/PreAggregate.Avg_Call_Volume_Past_Week1)*100 as 'Percentage_Variance'
from PreAggregate
cross apply (
select case
when Call_Volume_This_hr < Avg_Call_Volume_Past_Week1 then 0
else Call_Volume_This_hr -Avg_Call_Volume_Past_Week1
end 'Variance'
) InlineFunction;
I think your two commented lines should be as follows:
( ( SUM(CASE WHEN create_time >= #w0s
AND create_time < #w0e THEN 1
ELSE 0
END) ) - ( SUM(CASE WHEN create_time >= #w1s
AND create_time < #w1e THEN 1
ELSE 0
END) / 7.0 ) ) AS Variance,
( ( CASE WHEN ( ( SUM(CASE WHEN create_time >= #w0s
AND create_time < #w0e THEN 1
ELSE 0
END) ) - ( SUM(CASE WHEN create_time >= #w1s
AND create_time < #w1e THEN 1
ELSE 0
END) / 7.0 ) > 0 ) THEN 1
ELSE 0
END ) / ( SUM(CASE WHEN create_time >= #w1s
AND create_time < #w1e THEN 1
ELSE 0
END) / 7.0 ) * 100.0 ) AS Percentage_Variance
However without a fiddle or some structure and data I was unable to test it beyond correct syntax. Essentially the outermost CASE statements where the problem. They needed to be removed.

Appending Date in SQL Function

I am trying to get the correct year based on the current date and append to the fiscal year months date part but I am getting an error that it is not an integer. Ideas or thoughts?
`ALTER FUNCTION [dbo].[fn_CS_IssuedMODs] (#currentDate DATE)
RETURNS TABLE
AS
RETURN
(SELECT cs.Specialist, CASE WHEN COUNT(mn.mod_number_id) IS NULL
THEN 0 ELSE COUNT(mn.mod_number_id) END AS IssuedMODS,
cs.user_certificateSerialNumber
FROM dbo.tbl_modificationNumbers AS mn RIGHT OUTER JOIN
dbo.vw_ContractSpecialists AS cs ON mn.mod_specialist_id = cs.user_certificateSerialNumber
WHERE (mn.statusID = 10) AND effective_date between '10/1/'+DATEPART(YEAR,#currentDate)
+ CASE WHEN DATEPART(MONTH, #CurrentDate) >= 10 THEN -1 ELSE 0 END AND '09/30/'+DATEPART (YEAR,#currentDate)
GROUP BY cs.Specialist, cs.user_certificateSerialNumber`
lets create some variables for testing:
DECLARE #currentDate DATETIME = '4/2/2014'
DECLARE #FiscalYearStart DATETIME
DECLARE #FiscalYearEnd DATETIME
Now we are going to check whether the current date is before or after October 1 and if so, we are going to start the fiscal year using the previous year, otherwise we are in the new fiscal year.
SELECT #FiscalYearStart =
(
CASE
WHEN DATEPART(MONTH, #currentDate) < 10 THEN
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate) - 1, 0))
ELSE
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate), 0))
END
),
#FiscalYearEnd =
(
CASE
WHEN DATEPART(MONTH, #currentDate) < 10 THEN
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate), 0))
ELSE
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate) + 1, 0))
END
)
SELECT #FiscalYearStart As FiscalYearStart, #FiscalYearEnd As FiscalYearEnd
Output:
FiscalYearStart FiscalYearEnd
2013-10-01 00:00:00.000 2014-10-01 00:00:00.000
Now you can use effective_date >= #FiscalYearStart AND effective_date < #FiscalYearEnd in your query to pull the correct data for the year.
Instead of datepart() use either datename(). Or, do an explicit cast() to a string.
The + operator is overloaded in SQL Server. When it encounters a number, it is treated as addition -- and you can't add strings.
I can't figure out what your code is supposed to be doing. There are probably better ways to accomplish what you want. For starters, you should use ISO standard date formats (YYYYMMDD or YYYY-MM-DD) for constants.
Try this:
ALTER FUNCTION [dbo].[fn_CS_IssuedMODs] (#currentDate DATE)
RETURNS TABLE
AS
RETURN
(SELECT cs.Specialist, CASE WHEN COUNT(mn.mod_number_id) IS NULL
THEN 0 ELSE COUNT(mn.mod_number_id) END AS IssuedMODS,
cs.user_certificateSerialNumber
FROM dbo.tbl_modificationNumbers AS mn RIGHT OUTER JOIN
dbo.vw_ContractSpecialists AS cs ON mn.mod_specialist_id = cs.user_certificateSerialNumber
WHERE (mn.statusID = 10) AND effective_date between Cast(DATEPART(YEAR, DateAdd(Year, CASE WHEN DATEPART(MONTH, #CurrentDate) >= 10 THEN -1 ELSE 0 END, #currentDate)) as varchar) + '-10-1'
AND Cast(DATEPART(YEAR,#currentDate) as varchar) + '-09-30'
GROUP BY cs.Specialist, cs.user_certificateSerialNumber

How To Get Non-Null,Non-0 for Average with SqlServer Select

**Note: I need to go a little further and add NULLIF(0 or 5). I wrote a short post about my answer here:
http://peterkellner.net/2013/10/13/creating-a-compound-nullif-in-avg-function-with-sqlserver/
but am not happy with my solution)
I've got a table with results where attendees type in estimated attendance to a course. If they type 0 or leave it empty, I want ignore that and get the average of values typed in. I can't figure out how to add that constraint to my AVG function without having a where clause for the entire SQL. Is that possible? My code looks like this: (EstimatedNumberAttendees is what I'm going after).
SELECT dbo.SessionEvals.SessionId,
AVG(Cast (dbo.SessionEvals.CourseAsWhole as Float)) AS CourseAsWholeAvg,
COUNT(*),
COUNT(case
when dbo.SessionEvals.InstructorPromptness = 'On Time' then 1
else null
end) AS SpeakerOnTime,
COUNT(case
when dbo.SessionEvals.InstructorPromptness = 'Late' then 1
else null
end) AS SpeakerLate,
COUNT(case
when dbo.SessionEvals.InstructorPromptness = 'NoShow' then 1
else null
end) AS SpeakerNoShow,
COUNT(case
when dbo.SessionEvals.PercentFull = '10% to 90%' then 1
else null
end) AS PercentFull10to90,
COUNT(case
when dbo.SessionEvals.PercentFull = '> 90%' then 1
else null
end) AS PercentFullGreaterThan90,
COUNT(case
when dbo.SessionEvals.PercentFull = ' < 10% Full ' then 1
else null
end) AS PercentFullLessThan10,
AVG(Cast (dbo.SessionEvals.EstimatedNumberAttendees as Float)) AS
EstimatedAttending
FROM dbo.Sessions
INNER JOIN dbo.SessionEvals ON (dbo.Sessions.Id =
dbo.SessionEvals.SessionId)
WHERE dbo.Sessions.CodeCampYearId = 8
GROUP BY dbo.SessionEvals.SessionId
AVG omits NULLs. Therefore make it treat 0s as NULLs. Use NULLIF for that:
...
AVG(NULLIF(Cast (dbo.SessionEvals.CourseAsWhole as Float), 0)) AS CourseAsWholeAvg,
...
AVG(NULLIF(Cast (dbo.SessionEvals.EstimatedNumberAttendees as Float), 0)) AS EstimatedAttending
...
You can try to use an inner query to get the same sessions but exclude zero and null:
SELECT dbo.SessionEvals.SessionId,
(
SELECT AVG(SE1.CourseAsWhole)
FROM dbo.SessionEvals SE1
WHERE SE1.SessionId = dbo.SessionEvals.SessionId
AND ISNULL(SE1.CourseAsWhole, 0) <> 0
) AS CourseAsWholeAvg,
COUNT(*),
COUNT(case
when dbo.SessionEvals.InstructorPromptness = 'On Time' then 1
else null
end) AS SpeakerOnTime,
COUNT(case
when dbo.SessionEvals.InstructorPromptness = 'Late' then 1
else null
end) AS SpeakerLate,
COUNT(case
when dbo.SessionEvals.InstructorPromptness = 'NoShow' then 1
else null
end) AS SpeakerNoShow,
COUNT(case
when dbo.SessionEvals.PercentFull = '10% to 90%' then 1
else null
end) AS PercentFull10to90,
COUNT(case
when dbo.SessionEvals.PercentFull = '> 90%' then 1
else null
end) AS PercentFullGreaterThan90,
COUNT(case
when dbo.SessionEvals.PercentFull = ' < 10% Full ' then 1
else null
end) AS PercentFullLessThan10,
AVG(Cast (dbo.SessionEvals.EstimatedNumberAttendees as Float)) AS
EstimatedAttending
FROM dbo.Sessions
INNER JOIN dbo.SessionEvals ON (dbo.Sessions.Id =
dbo.SessionEvals.SessionId)
WHERE dbo.Sessions.CodeCampYearId = 8
GROUP BY dbo.SessionEvals.SessionId
SQL AVG function will by default ignore null values so you need to only exclude the 0s. Your AVG code can be changed to below:
AVG(nullif( Cast(dbo.SessionEvals.CourseAsWhole as Float), 0) AS CourseAsWholeAvg

Resources