Returning integer value that represents the first non-null field - sql

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.

Related

How to translate nested CASE statement in WHERE clause

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!

Select all records where function is false

I have a validation function that I'd like to run on each id in a table, and return only those ids which are invalid. Code that I've tried:
select myID
from myDB.dbo.myTable
where (myDB.dbo.validateID(myID) = 0)
Where myDB.dbo.validateID is a scalar-valued function.
This works but returns null for all the valid IDs - I want to only return the invalid ones. What is the most efficient way to return all the invalid rows using this function?
Update:
The validateID function returns 1 if the ID is valid, 0 if it isn't.
My code above returns null if the ID is valid, and the ID if it's not. I want it to instead only return the invalid IDs, without all the null results.
select myID
from myDB.dbo.myTable
where (myDB.dbo.validateID(myID) = 0)
This works but returns null for all the valid IDs
That is simply not possible. If you select myID, you get myID, so if you get null, then myID must be null, and your validateID function is detecting that as invalid.
If your function should treat null as a valid ID, you need to fix your function so that myDB.dbo.validateID(null) returns 1.
If your function should treat null as neither a valid nor an invalid ID, you need to fix your function so that myDB.dbo.validateID(null) returns null.
If your function should treat null as an invalid ID, but you still want to exclude null results, when just add a condition myID is not null to your selection.
I tried the same logic but am getting proper output as expected. Am getting rows returned only when function outputs 0 (ie) Invalid records. I don't see NULL values for valid records.
create table dbo.Contracts1(id int,name varchar(50),pric int)
INSERT INTO dbo.Contracts1
VALUES ( 1,'invalid',40000),
(2,'valid',50000),
(3,'valid',35000),
(4,'invalid',40000)
CREATE FUNCTION Testt(#id INT)
returns INT
AS
BEGIN
DECLARE #ret INT
IF EXISTS (SELECT 1
FROM dbo.Contracts1
WHERE name = 'Valid'
AND id = #id)
SELECT #ret = 1
ELSE
SELECT #ret = 0
RETURN #ret
END
SELECT *
FROM dbo.Contracts1
WHERE dbo.Testt(id) = 0
OUTPUT
id name pric
-- ------- -----
1 invalid 40000
4 invalid 40000

Sql Server 2012 Stored procedure query with where clause is null or input parameter

I want to create a stored procedure that uses a an input parameter in the where clause. The parameter will either be text or null.
I.e. suppose I have a table [tbl]
Status val
NULL 158
Acc 155
Acc 152
Null 24
Rej 34
In SQL server the null values can be found by using
WHERE Status is NULL
I also want the stored procedure to be able to handle a case when the parameter equals a specific value.
WHERE Status = #parameter
How do I combine both of these cases? I want something like this.
CREATE PROC test #parameter VARCHAR(10) = ''
SELECT *
FROM [tbl]
IF (#parameter is NULL OR #parameter = '') THEN WHERE STATUS is Null
ELSE WHERE STATUS = #parameter
WHERE (ISNULL(#parameter,'') = '' AND Status IS NULL)
OR Status = #parameter
The top half can only ever evaluate to true if #Status isn't NULL. Otherwise it falls back to the bottom half.
ISNULL(#Status,'') = '' is just the same as saying #Status IS NULL OR #Status = '', and if that's the case then it matches rows where Status IS NULL.
Looking at your pseudocode, you were pretty much there (things I added are in bold):
IF( (#parameter is NULL OR #parameter = '')THEN WHEREANDSTATUS is Null)
ELSE WHERE OR STATUS = #parameter
Just realised this might be better:
WHERE ISNULL(#parameter, '') = ISNULL(Status, '')
It will work the same, but I'm not sure if the ISNULL(Status, '') will stop indexes from being used, so it'd be worth checking the execution plan.
Just include both the condition in your WHERE clause such that it will fetch rows where status having null's or status having specific value
WHERE Status is NULL
OR Status = #parameter

How do I incorporate "where [binary] is not null" in a T-SQL select statement?

I have a stored procedure that I need to filter rows that have a binary value and return only rows that are not null in the binary column.
When I execute this stored procedure:
create procedure sp_GetGraduatingStudentDataByYear
(
#year nvarchar(4)
)
as
select * from Cohort_Graduation_Student_Data where Exp_Grad_Year = #year and Off_Track != null
go
I get no results.
How can I alter this script to return the rows with a null value in the binary column?
This is not because it's a binary column, but because null in SQL should not be compared to anything. Essentially, Off_Track != null condition filters out all rows - all checks for column = null and column != null always evaluate to false.
Use is not null instead:
select *
from Cohort_Graduation_Student_Data
where Exp_Grad_Year = #year and Off_Track is not null

SQL Data Filtering approach

I have a stored procedure that receives 3 parameters that are used to dynamically filter the result set
create proc MyProc
#Parameter1 int,
#Parameter2 int,
#Paremeter3 int
as
select * from My_table
where
1 = case when #Parameter1 = 0 then 1 when #Parameter1 = Column1 then 1 else 0 end
and
1 = case when #Parameter2 = 0 then 1 when #Parameter2 = Column2 then 1 else 0 end
and
1 = case when #Parameter3 = 0 then 1 when #Parameter3 = Column3 then 1 else 0 end
return
The values passed for each parameter can be 0 (for all items) or non-zero for items matching on specific column.
I may have upwards of 20 parameters (example shown only has 3). Is there a more elegant approach to allow this to scale when the database gets large?
I am using something similar to your idea:
select *
from TableA
where
(#Param1 is null or Column1 = #Param1)
AND (#Param2 is null or Column2 = #Param2)
AND (#Param3 is null or Column3 = #Param3)
It is generally the same, but I used NULLs as neutral value instead of 0. It is more flexible in a sense that it doesn't matter what is the data type of the #Param variables.
I use a slightly different method to some of the ones listed above and I've not noticed any performance hit. Instead of passing in 0 for no filter I would use null and I would force it to be the default value of the parameter.
As we give the parameters default values it makes them optional which lends itself to better readability when your calling the procedure.
create proc myProc
#Parameter1 int = null,
#Parameter2 int = null,
#Paremeter3 int = null
AS
select
*
from
TableA
where
column1 = coalesce(#Parameter1,column1)
and
column2 = coalesce(#Parameter2, column2)
and
column3 = coalesce(#Parameter3,column3)
That said I may well try out the dynamic sql method next time to see if I notice any performance difference
Unfortunately dynamic SQL is the best solution performance/stability wise. While all the other methods commonly used ( #param is not or Col = #param, COALESCE, CASE... ) work, they are unpredictable and unreliable, execution plan can (and will) vary for each execution and you may find yourself spending lots of hours trying to figure out, why your query performs really bad when it was working fine yesterday.