CASE Statement - Batch - sql

Looking at making a basic scoring system based on questions answered.
The questions that are asked, all have 3 possible answers which are identical per question.
Currently I have:
SELECT [form].[Name],
[form].[TimeTag1],
(100 - [Form].[Field2] - [Form].[Field3] - [Form].[Field4] - [Form].[Field5]) AS [Total]
FROM
(SELECT
af.Name
,af.TimeTag1
,CASE WHEN [af].[aField46] = 'Checked Ok' THEN CONVERT(INT, '0')
WHEN [af].[aField46] = 'Rectified On-Site' THEN CONVERT(INT, '5')
WHEN [af].[aField46] = 'Failed' THEN CONVERT(INT, '5')
END AS [Field2]
,CASE WHEN [af].[aField2] = 'Checked Ok' THEN CONVERT(INT, '0')
WHEN [af].[aField2] = 'Rectified On-Site' THEN CONVERT(INT, '5')
WHEN [af].[aField2] = 'Failed' THEN CONVERT(INT, '5')
END AS [Field3]
,CASE WHEN [af].[aField4] = 'Checked Ok' THEN CONVERT(INT, '0')
WHEN [af].[aField4] = 'Rectified On-Site' THEN CONVERT(INT, '5')
WHEN [af].[aField4] = 'Failed' THEN CONVERT(INT, '5')
END AS [Field4]
,CASE WHEN [af].[aField5] = 'Checked Ok' THEN CONVERT(INT, '0')
WHEN [af].[aField5] = 'Rectified On-Site' THEN CONVERT(INT, '5')
WHEN [af].[aField5] = 'Failed' THEN CONVERT(INT, '5')
END AS [Field5]
FROM vAdvF_167 af) AS [Form]
This works but I'm looking for a more efficient way of completing this task as there will be over 100 potential questions which could be answered.
Is this the main way to achieve what I want or is there a quicker/more efficient way as this will be added to SSRS Reporting once done.
Edits:
The Table cannot be changed.
Those 3 answers are the only answers ever available to a user on all Q's

I'm not sure if it's going to perform better, but a translation table will make the query more readable, at the very least:
DECLARE #Translate AS TABLE
(
string varchar(17),
number int
)
INSERT INTO #Translate VALUES
('Checked Ok', 0),
('Rectified On-Site', 5),
('Failed', 5)
SELECT [form].[Name],
[form].[TimeTag1],
(100 - [Form].[Field2] - [Form].[Field3] - [Form].[Field4] - [Form].[Field5]) AS [Total]
FROM
(SELECT
af.Name
,af.TimeTag1
,f46.number AS [Field2]
,f2.number AS [Field3]
,f4.number AS [Field4]
,f5.number AS [Field5]
FROM vAdvF_167 af
JOIN #Translate f46 ON [af].[aField46] = f46.string
JOIN #Translate f2 ON [af].[aField2] = f2.string
JOIN #Translate f4 ON [af].[aField4] = f4.string
JOIN #Translate f5 ON [af].[aField5] = f5.string) AS [Form]

If you can't change table, one of the thing you can optimize (every) case in this way (e.g.) (It can help you to generate using information_schema):
,CASE WHEN [af].[aField46] = 'Checked Ok' THEN 0
WHEN [af].[aField46] IN('Rectified On-Site', 'Failed' THEN 5
END AS [Field2]

Related

SQL Server : how to use a variable with multiple variables in a case statement in a where clause

I am attempting to build a simple lookup table that will have its own web page display. I am providing simple search variables, and one of those variables is Status. I am looking for them to be able to Choose from Active, Inactive, or both, I am looking to easily look for both at once. There are other status in the database such as 'D' for a soft delete that I do not want returned at all.
Declare #stat nvarchar(5) = 3
Select [Status]
from tableUser
where [Status] in (CASE #stat
WHEN 1 THEN 'A'
WHEN 2 THEN 'I'
WHEN 3 THEN 'A','I'
END)
The above is what I have tried.
Just use boolean logic:
WHERE (#stat IN (1, 3) AND Status = 'A') OR
(#stat IN (2, 3) AND Status = 'I')
You want the CASE statement to produce an expression, but that's not how it works. CASE statements produce values (which can be included as expressions, but the expressions must still reduce to values at query compile time). 'A','I' does not reduce to a value, so you cannot use it as the result of a CASE statement.
Instead, write the condition more like this:
WHERE 1 = CASE WHEN #stat = 1 AND [Status] = 'A' THEN 1
WHEN #stat = 2 AND [Status] = 'I' THEN 1
WHEN #stat = 3 AND [Status] IN ('A', 'I') THEN 1
ELSE 0 END
or remove the CASE expressions and build all that directly into the WHERE clause:
WHERE ( (#stat = 1 AND [Status] = 'A')
OR (#stat = 2 AND [Status] = 'I')
OR (#stat = 3 AND [Status] IN ('A', 'I'))
)

Display column name based on status 0 or 1

I don't understand how to achieve column name based on status, I think it's not possible to display multiple column names for the same but I have this requirement to display column name either 'Success' or 'Fail' and both status never be fetched, I have to select only one at a time either 0 or 1 based on where condition, value is defined in the variable.
CREATE TABLE #sqlJobHistory(jobId INT, jobStatus INT)
INSERT INTO #sqlJobHistory VALUES
(1, 0),
(2, 0),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1)
I want to display my second column name based on status, So far I tried the commented line but :(
SELECT MAX(jobId) maxJobId,
COUNT(*) AS 'Failure' --AS CASE WHEN jobStatus = 0 THEN 'Fail' ELSE 'Success' END
FROM #sqlJobHistory
WHERE jobStatus = 0
GROUP BY jobStatus
Can you please help he what's the proper way of achieving the desired output if it is possible, if not possible then extremely sorry for wasting your valuable time.
Yes, it can be possible through dynamic query, if I understand correctly, your second column name is 'Success' if Status = 1 else 'Fail'
DECLARE #status INT, #sql VARCHAR(500)
SET #status = 1 --It can be 0 or 1 and I think you already have this variable
SET #sql ='
SELECT MAX(jobId) maxJobId,
COUNT(*) AS ' +
CASE WHEN CAST(#status AS VARCHAR)= 0 THEN 'Failure' ELSE 'Success' END + '
FROM #sqlJobHistory
WHERE jobStatus = ''' + CAST(#status AS VARCHAR) + '''
GROUP BY jobStatus'
EXEC(#sql)

Logic to get only one row in result set

am stuck at point to build the logic. Can anyone help me ?
requirement:
For privacyType = 'Primary Address' if there is >1 row where SI0_ADDR.ADDR_TYPE_CODE = 'M', display the row where currentDate() is between SI0_ADDR.ADDR_EFF_DATE and SI0_ADDR.ADDR_EXPR_DATE.
my query is :
select STU_ID
,case when Privacyflag = '' then 'N'
else Privacyflag
end Privacyflag
,type from (
select a.STU_ID,Privacyflag,a.type
,ROW_NUMBER() OVER ( ORDER BY ADDR_EXPR_DATE DESC) disp_nm from (
select ad.STU_ID, case when ad.ADDR_TYPE_CODE = 'M' then ad.ADDR_PRIVACY_FLAG
when ad.ADDR_TYPE_CODE='' then 'N'
end Privacyflag, 'Primary Phone' type
, case when ADDR_EXPR_DATE = '1900-01-01' then '2100-12-31'
else ADDR_EXPR_DATE
end as ADDR_EXPR_DATE
from SI0_ADDR ad
where ad.STU_ID = #studentid ) a
where Privacyflag is not null
) ab
where ab.disp_nm = '1'
This logic is not working in some of the cases
You're not qualifying by the date in any way, which was part of your requirements. Your query doesn't need so many nested sub-queries. The PrivacyFlag doesn't seem to be part of your question, so I didn't work it into this example. And your example shows Primary Phone, but your question talks about Primary Address, so I'm not sure how to work that out.
In any case, here is a very simple example that shows the data, and how to pull the one record with the latest expiration date, and also the latest effective date (in case there are two records with the same expiration date).
create table #temp (stu_id int, type varchar(10), address varchar(50), eff_date datetime, expr_date datetime)
insert into #temp values
(1, 'primary', '123 NW 52nd', '1/1/2016', '1/1/1900'),
(1, 'primary', '942 SE 33rd', '1/2/2016', '12/31/2016'),
(1, 'primary', '721 SW 22nd', '4/1/2015', '1/1/1900')
select top 1 *
from (
select stu_id
,type
,address
,eff_date
,case when expr_date = '1/1/1900' then '12/31/2100' else expr_date end as expr_date
from #temp
where stu_id = 1
and type = 'primary'
) as a
where getdate() between eff_date and expr_date
order by a.expr_date desc, a.eff_date desc
drop table #temp
You could even do it with zero sub-queries, but then you'd need to duplicate the case statement into the where and order by parts of the query:
select top 1
stu_id
,type
,address
,eff_date
,case when expr_date = '1/1/1900' then '12/31/2100' else expr_date end as expr_date
from #temp
where stu_id = 1
and type = 'primary'
and getdate() between eff_date and case when expr_date = '1/1/1900' then '12/31/2100' else expr_date end
order by case when expr_date = '1/1/1900' then '12/31/2100' else expr_date end desc
,eff_date desc
With such a small dataset, the showplan didn't indicate which query would be most efficient; it showed the same identical query plan for both queries, except the first select statement took 7 CPU cycles to compile the plan and the second select statement took 2 CPU cycles to compile the plan. You may want to test against a larger dataset on real tables with indexes, etc., and see which performs the best.

How to select on stored procedure with filter have more than 3 value

I would like to know that how to select when using stored procedure with filter as combobox in website have more than 3 value
Exp: I would like to select match listing. and filter is "IsFinish" with value (All, Yes, No)
DECLARE #IsFinish INT -- 1: All 2: Yes 3: No
SELECT * FROM MATCH
WHERE [Status] = ?
Status values: F: Finished C: Canceled L: Live N: Non-Live P:Pause X:(Close) Waiting Confirm
When select All the result will return all status.
When select Yes the result will return F & X.
When select No the result will return N, L, C, P.
I would like filter them by once select.
How can I do it?
Prepare Data
CREATE TABLE [Match] ([Status] CHAR(1) PRIMARY KEY, [Wording] VARCHAR(50));
INSERT INTO [Match] ([Status], [Wording]) VALUES
('F', 'Finished'),
('C', 'Canceled'),
('L', 'Live'),
('N', 'Non-Live'),
('P', 'Pause'),
('X', '(Close) Waiting Confirm');
Prepare Procedure
CREATE PROCEDURE [FilterMatch] (#IsFinish INT = 1 /* 1: All (Default) 2: Yes 3: No */)
AS
SELECT * FROM [Match]
WHERE #IsFinish = 1 OR
(#IsFinish = 2 AND [Status] In ('F','X')) OR
(#IsFinish = 3 AND [Status] In ('N','L','C','P'));
Run
Exec FilterMatch;
Exec FilterMatch 2;
Exec FilterMatch #IsFinish = 3;
In SQL-Server you can achieve It in following:
where [status] LIKE (case #IsFinish when 1 then '%' end) or
[status] = (case #IsFinish when 2 then 'F' end) or
[status] = (case #IsFinish when 2 then 'X' end) or
[status] = (case #IsFinish when 3 then 'N' end) or
[status] = (case #IsFinish when 3 then 'L' end) or
[status] = (case #IsFinish when 3 then 'C' end) or
[status] = (case #IsFinish when 3 then 'P' end)
Use IN and CASE and OR. Something like:
WHERE isFinish = 1
OR isFinish = 2 AND Status IN ('F','X')
OR isFinish = 3 AND Status IN ('N','L','C','P')
I have tried to using where case when with
where case when [status] IN ('L','N','F','X','C','P') then 0
when [status] in ('F','X') THEN 1
when [Status] IN ('L','N','C','P') THEN 2 END = #IsFinish
But I'm not feeling It look good.
I look forward to hearing from you in the nearest time.

Execution of sql query takes a long time

I have this problem with my query that it is really slow. I am using MSSQL server 2008 and have 3 DB with hundreds of sample data in it. The query will return a name and value and a computed percentage based on the 3 DBs. But the query I have will take almost 10mins to execute which is really a long time to take. I am still learning SQL and still not that good so I think the query I have is not using the best practice and not organized. Can anybody point to me where or how I can improve my query to run faster?
SELECT data.Ret,
case
when #group_by= 'site' OR (#group_by='attribute' AND #attribute_id = '5') and (data.rowid % 50) = 0 then (data.rowid / 50)-1
when #group_by= 'site' OR (#group_by='attribute' AND #attribute_id = '5') then (data.rowid / 50)
else 0 end as batchStore
,data.MajorName,data.MinorName,data.MajorVal,data.MinorVal,data.Version
,data.A_Percent,data.T_Percent,data.F_Percent
from
(
SELECT report.Ret,
CASE when #group_by= 'site' OR (#group_by='attribute' AND #attribute_id = '5')
then row_number() over (PARTITION BY report.Ret,report.Version order by report.Ret, report.MajorName)
else 0 end as rowid
,report.MajorName,report.MinorName,report.MajorVal,report.MinorVal,report.Version
,report.GTotal_A,report.GTotal_T,report.GTotal_F
,ISNULL(sum(report.Abn) / NULLIF(cast(report.GTotal_A as decimal),0),0) * 100 as A_Percent
,ISNULL(sum(report.Trn) / NULLIF(cast(report.GTotal_T as decimal),0),0) * 100 as T_Percent
,ISNULL(sum(report.Fld)/ NULLIF(cast(report.GTotal_F as decimal),0) * 100,0) as F_Percent
From
(
Select
CASE #group_by
WHEN 'object' THEN csl.s_name
WHEN 'site' THEN csl.s_name
WHEN 'year' THEN CAST(YEAR(dy.Day_Stamp) AS VARCHAR(50))
WHEN 'attribute' THEN CAST(coalesce(attrib.AttributeName,'') AS VARCHAR(50))
ELSE ''
END as MajorName,
CASE #group_by
WHEN 'object' THEN l.l_name
WHEN 'site' THEN ''
WHEN 'attribute' THEN CAST(coalesce(attrib.AttributeName,'') AS VARCHAR(50))
ELSE ''
END as MinorName,
CASE #group_by
WHEN 'object' THEN csl.s_name
WHEN 'site' THEN csl.s_name
WHEN 'year' THEN CAST(YEAR(dy.Day_Stamp) AS VARCHAR(50))
WHEN 'attribute' THEN CAST(coalesce(attrib.AttributeValue,'') AS VARCHAR(50))
ELSE ''
END as MajorVal,
CASE #group_by
WHEN 'object' THEN l.l_name
WHEN 'site' THEN ''
WHEN 'attribute' THEN CAST(coalesce(attrib.AttributeValue,'') AS VARCHAR(50))
ELSE ''
END as MinorVal,
csl.Cust_Name as Ret,l.SWVersion as Version
,d.Abn,d.Trn,d.Fld,data.GTotal_A ,data.GTotal_T,data.GTotal_F
From db_mon.dbo.CustSL csl
join db_tax.dbo.vwLane l
on (l.externalid = csl.custsl_id)
join db_mon.dbo.DaySummary dy
on (dy.Str = csl.s_name and dy.Lne = csl.l_name and year(dy.day_stamp) = year(#time_start_date) and year(dy.day_stamp) =year(#time_end_date))
Left Outer Join
(
Select a.id As AttributeId, a.attribute_name As AttributeName,
(Case When a.attribute_value_type = 'string' Then ea.string_value
Else (Case When a.attribute_value_type = 'integer' Then cast(ea.integer_value as nvarchar(100))
Else (Case When a.attribute_value_type = 'date' Then cast(ea.date_value as nvarchar(100))
Else (Case When a.attribute_value_type = 'boolean' Then cast(ea.boolean_value as nvarchar(100))
Else (Case When a.attribute_value_type = 'entity' Then cast(ea.ref_entity_id as nvarchar(100)) Else null End)
End)
End)
End)
End) As AttributeValue,
e.id As EntityId
From db_tax.dbo.entity_type et
Inner Join db_tax.dbo.entity As e on et.id = e.entity_type_id
Inner Join db_tax.dbo.entity_attribute As ea on e.id = ea.entity_id
Inner Join db_tax.dbo.attribute As a on ea.attribute_id = a.id
WHERE et.entity_type_name in ('Sticker','Label') And
a.id = (case WHEN #attribute_id = '' then 1 else cast(#attribute_id as int) end)
) AS attrib
On attrib.EntityId = l.L_Id
inner join db_mon.dbo.DaySummary d
on (csl.Cust_Name = d.Ret and csl.s_name = d.stckr and csl.l_name = d.label and l.SWVersion = d.Version)
join (
SELECT Ret,version,sum(Abn) as GTotal_A,sum(Trn) as GTotal_T,sum(Fld) as GTotal_F
from db_mon.dbo.DaySummary
where day_stamp >= #time_start_date and day_stamp <=#time_end_date
GROUP BY Ret,version
) data
on (d.Ret = data.Ret and l.SWVersion = data.Version)
WHERE (CHARINDEX(',' + CONVERT(VARCHAR,l.S_Id) + ',','xxx,' + #entities + ',xxx')>0 OR CHARINDEX(',' + CONVERT(VARCHAR,l.L_Id) + ',','xxx,' + #entities + ',xxx')>0)
and d.day_stamp >= #time_start_date
and d.day_stamp <=#time_end_date
) As report
Group By report.Ret,report.Version,report.MajorName,report.MinorName,report.MajorVal,report.MinorVal
,report.GTotal_A,report.GTotal_T,report.GTotal_F
)data
order By data.Ret,data.Version,batchStore,data.MajorName,data.MinorName,data.MajorVal,data.MinorVal
Does using a lot of join causes the slow execution?
SUB Select queries will always be slower then proper joins.
You are running 3 sub selects deep. This is going to impact your performance regardless of changing indexes etc.
You need to rewrite the whole thing to stop using sub selects.