How to translate nested CASE statement in WHERE clause - sql

I have a stored procedure that was not written by me.
Could you please help me translate CASE statement in WHERE clause?
--Declaring the parameter for SP
DECLARE
#CompanyGuids varchar(8000) = '29634AF7-D0A2-473D-9574-405C23E10F02'
--Declaring table variable that will contain only CompanyGuid
DECLARE #CompanyGuidsTbl TABLE(Guid uniqueidentifier)
IF #CompanyGuids IS NULL
BEGIN
INSERT INTO #CompanyGuidsTbl
SELECT DISTINCT CompanyGuid FROM tblCompanies
END
ELSE
BEGIN
INSERT INTO #CompanyGuidsTbl
SELECT * FROM dbo.StringOfGuidsToTable(#CompanyGuids,',')
END
--Select statement
SELECT Col1,
Col2,
Col3
FROM MyTable1 INNER JOIN MyTable2
/* this is where I am confused */
WHERE
CASE WHEN #CompanyGuids IS NOT NULL
THEN
CASE WHEN tblCompanies.CompanyGuid in (SELECT Guid FROM #CompanyGuidsTbl)
THEN 1 ELSE 0 END
ELSE 1
END = 1
Correct me if I'm wrong:
"So if the parameter #CompanyGuids is NOT NULL, then we are checking if table #CompanyGuidsTbl has assigned parameter and if it does - then we gonna use it, but if it does not - ??? "
Maybe there is a way to rewrite it?
Thanks

A poorly constructed statement for sure, but yes it is ultimately checking a truth statement where 1 = 1. First checks for an empty variable, then if the variable is not empty it checks if the CompanyGUID in tblCompanies is in the list supplied in the variable, returning 1 if it is found (thus 1 = 1 is true so the record is matched), or if it is not found (in which case 0 = 1, which is false so the record is not matched). Awful stuff!

Related

Is there a way to use in or = with a case statement in the where clause in sql?

I have a stored procedure that may or may not get a string list of int ids. When it doesn't get it the value is: ' '. Other wise its something like this: '500,507,908'
I'm trying to use it like this:
select ID as projectTeamId, Employee_ID, Supervisor_ID
from ProjectTeam
where Project_ID = #projectId and IsDeleted = 0 and
ID in (CASE #stringList WHEN '' THEN ID ELSE (SELECT * from TurnListStringIntoTable(#stringList)) END)
to get a result set but it errors out with this code when the string list comes in blank:
An error has occurred while processing Report 'MassReleaseHoursReport':
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I know its an issue where id needs to = id instead of being in id. Any ideas on how I can have the case statement work with #stringList = '' then id = id else id in (SELECT * from TurnListStringIntoTable(#stringList))?
TurnListStringIntoTable returns a table from a string list which in my case is just the project Team ID
I would recommend boolean logic rather than a case expression:
where
Project_ID = #projectId
and IsDeleted = 0
and (
#stringList = ''
or id in (select * from TurnListStringIntoTable(#stringList))
)
Unrelated side note: if you are running SQL Server, as I suspect, and your version is 2016 or higher, you can use built-in function string_split() instead of your customer splitter.
Sure!
All you have to do is use the parameterless flavor of case:
select *
from my_table t
where t.col_1 = case
when #var in (1,2,3) then "foo"
when #var = 4 then "bar"
when #var in (5,6,7) then "baz"
else "bat"
end
One might note that the when expressions are not limited to looking at the same variable in any way. The only requirement is that they have to be a boolean expression. They're evaluated in order top to bottom. The first when clause that evaluates to true wins and it's then value is returned.
If there's no else and evaluation fails to find a match, the result value is null.
Your problem though, is that case expressions
return a single value, and
that value must be of the same type. Can have it returning a string in some cases and a table variable in another.
So... your where clause should look something like this:
where ...
and 'true' = case
when #stringList = '' then 'true'
when ID in ( select *
from TurnListStringIntoTable(#stringList)
) then 'true'
else 'false'
end
You'll probably find, too, that invoking a user-defined function to convert a comma-delimited string into a table variable within the where clause is probably a Bad Idea™ due to the performance impact that that will have.
You'd be better off to move your TurnListStringIntoTable call outside of the select statement, thus:
declare #list = TurnListStringIntoTable(#stringlist)
select ...
from ProjectTeam pt
where . . .
and #stringlist = ''
OR exists ( select * from #list x where x.ID = pt.ID )

Using the LIKE operator and "greater than" in a Case When expression

This is for a section in a unit test that I'm writing.
I am trying to say pass if any row in a column contains a certain string. So in words, what I want is "if the number of row that contain astring is greater than zero than pass the test".
I have something like the code below, but it fails saying that myVariable needs to be declared. What am I doing wrong?
DECLARE #myVariable BIT =
(
SELECT CASE
WHEN Count(Description) LIKE '%astring%' > 0
THEN
1
ELSE
0
END
FROM TABLE
SELECT #myVariable
I think you want:
DECLARE #myVariable BIT =
(SELECT (CASE WHEN Count(*) > 0 THEN 1 ELSE 0 END)
FROM TABLE
WHERE Description LIKE '%astring%'
);
I wouldn't recommend a bit for this. SQL Server doesn't really support booleans. Integers (or tinyints even) are usually easier to work with than bits.
Just:
DECLARE #myVariable BIT = (
SELECT MAX(CASE WHEN Description LIKE '%astring%' THEN 1 ELSE 0 END)
FROM mytable
);
This sets the variable to 1 if at least one row in the table has a Description that matches the pattern.
DECLARE #myVariable BIT=
(
SELECT IIF(ISNULL(count(*),0)>0,1,0)
FROM TABLE
WHERE EXISTS(SELECT * FROM TABLE WHERE Description LIKE '%astring%')
)
SELECT #myVariable AS myVariable

Why doesn't the Select statement assigns an empty string or null value if it doesn't return a result?

I have the following code:
declare #testValue nvarchar(50) = 'TEST';
select #testValue = 'NOTUSED' where 1 > 2;
select #testValue; -- Outputs 'TEST'
select #testValue = 'USED' where 2 > 1;
select #testValue; -- Outputs 'USED'
With the above, the first assignment is never used because the where clause fails. The second one is done properly and used is returned.
Why doesn't SQL return a null in this case and assigns a NULL value to #testValue after the first assignment where the where clause fails?
This is the expected behavior:
"If the SELECT statement returns no rows, the variable retains its present value. If expression is a scalar subquery that returns no value, the variable is set to NULL."
https://msdn.microsoft.com/en-us/library/ms187330.aspx
You can get around this in your example by using a subquery in the right side.
SELECT #testValue = (SELECT 'NOTUSED' where 1 > 2);
As for why it is this way, I cannot say for certain. Perhaps the entire #testValue = 'NOTUSED' is equating to NULL instead of only the right side 'NOTUSED' portion of the statement, and this prevents the parameter from being set. Not directly related but I can say it took me some time to grow confident with writing queries when NULLs are involved. You need to be aware of / familiar with the ANSI NULL spec and associated behavior.
This is the default behavior of SELECT.
When assigning a value to a variable using SELECT, if there is no value returned, SELECT will not make the assignment at all so the variable's value will not be changed.
On the other hand, SET will assign NULL to the variable if there is no value returned.
For more info
NULL is the ideal value you would like but the SQL engine is not clever enough, because some else may want empty string , ' ' in that situation or 0 or 1, you see. So no single default value is set. Best is set your own default value. You can see below
DECLARE #testValue NVARCHAR(50) = 'TEST';
SELECT #testValue = 'NOTUSED' WHERE 2 > 1;
IF 2 <> 1
SELECT #testValue = NULL;
SELECT #testValue; -- Outputs 'TEST'
SELECT #testValue = 'USED' WHERE 1 > 2;
SELECT #testValue; -- Outputs 'USED'
NULL in SQL is used to denote missing data or an unknown value. In this case the data is not missing, the value of #testValue is known, it is just failing an assignment condition, so it gets no new value.
If you were to change your initial assignment to be like this
declare #testValue nvarchar(50)
You would get NULL like below :
select #testValue = 'NOTUSED' where 1 > 2;
select #testValue; -- Outputs NULL
select #testValue = 'USED' where 2 > 1;
select #testValue; -- Outputs 'USED'
Don't be too disappointed your not getting NULL back in the your example. NULL is not easy to handle.
For example, you can not compare two NULL values, because instances of NULL are not equal. Consequently you also need to use special operators like ISNULL to check for it.
In general, NULL as a programming construct should be avoided in my opinion. This is a bit of area of contention across the programming languages. But consdier this, even the creator of null Tony Hoare, calls the creation of null his 'billion dollar mistake'.

Bit field in where clause

I have a situation where I want to search for a field in the where clause only if the bit variable is 1 else ignore it.
#Active bit = 0
select * from Foo where firstname = 'xyz' and
if(#active=1)
then search on the Active column else ignore the filtering on the Active column. How can I have that in a simple condition instead of checking each parameter seperately and then building the where clause
Just simple logic will usually suffice:
select * from Foo where firstname = 'xyz' and
(#Active = 0 or Active = <filter condition>)
For general advice about writing code for arbitrary search conditions, you could do worse than read Erland Sommarskog's Dynamic Search Conditions in T-SQL
it seems like Active is the Actual Column as well in your table.
using Case stmt you can make the search efficient as it will use appropriate indexes you may have on this table.
DECLARE #Active BIT=0
SELECT *
FROM Foo
WHERE firstname = 'a'
AND active = CASE WHEN #Active=1 THEN #Active ELSE Active END
How about:
DECLARE #Active bit
if #Active = 1
BEGIN
(select * from Foo where firstname = 'bar' and Active = --condition)
END
else
BEGIN
(select * from Foo where firstname = 'bar')
END
of course, something will have to set the value for #Active somewhere between the declaration and the if...else statement.
you can write this as below
select * from Foo where firstname = 'xyz' and (#isactive=0 or (some conditions))

Returning integer value that represents the first non-null field

I've created the following stored procedure:
ALTER PROCEDURE [dbo].[ExampleSP]
(
#SearchText NVARCHAR(4000),
#ID INT = NULL
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
deID,
deTitle
FROM tblDemo As de
LEFT JOIN tblLinkTable As lnk ON (lnk.ID = de.deID)
WHERE CONTAINS(cstKeywords, #SearchText)
AND ((#ID IS NULL) OR (lnk.ID = #ID))
GROUP BY deID,Title
ORDER BY de.Title
But I also need to be able to find the first field that is not null out of the following table columns:
deIntroText, deCompanyText, deTimetableText and deExampleText
And i need to do this for each record that is returned from the SELECT.
So I realise that i'd need to create a temporary column to store it in and i guess you'd need to use an IF statement like this:
IF deIntroText IS NOT Null
THEN TempFieldToReturn = 1
ELSE IF deCompanyText IS NOT Null
THEN TempFieldToReturn = 2
ELSE IF deTimetableText IS NOT Null
THEN TempFieldToReturn = 3
ELSE IF deExampleText IS NOT Null
THEN TempFieldToReturn = 4
So my question is - what is the best way to achieve this? Any examples would be appreciated.
No real shortcut - just use a CASE expression:
SELECT
/* Other Columns */
CASE
WHEN deIntroText IS NOT Null THEN 1
WHEN deCompanyText IS NOT Null THEN 2
WHEN deTimetableText IS NOT Null THEN 3
WHEN deExampleText IS NOT Null THEN 4
ELSE 5 END as OtherColumn
FROM
/* Rest of query */
This is a Searched CASE - there are actually two variants of CASE. I guessed at 5 if all of the columns are NULL - you might leave off the ELSE 5 portion, if you want a NULL result in such a case.