Call scalar function in CASE expression SQL Server 2012+ - sql

I am trying to emulate the PL/SQL DECODE() function in T-SQL:
CREATE FUNCTION [dbo].[fnDecode]
(
#condition AS BIT,
#trueVal AS VARCHAR(2000),
#falseVal AS VARCHAR(2000)
)
RETURNS VARCHAR(2000)
WITH EXEC AS CALLER AS
BEGIN
RETURN CASE #condition
WHEN 1 THEN #trueVal
ELSE #falseVal
END
END
GO
The PL/SQL fragment I'm trying to port to T-SQL is this:
CASE pcr.person_type
WHEN 'N'
THEN
npcr.first_name
|| DECODE (npcr.middle_name,
NULL, '',
' ' || npcr.middle_name)
|| ' '
|| npcr.last_name
WHEN 'L'
THEN
lpcr.name
END
I want to call the scalar function defined above in a CASE statement like this:
CASE pcr.person_type
WHEN 'N' THEN npcr.first_name
+ dbo.fnDecode(npcr.middle_name IS NULL, '', ' ' + npcr.middle_name)
+ ' '
+ npcr.last_name
WHEN 'L' THEN lpcr.name
END
I cannot store the result of the fnDecode() function into a variable because the rows are coming from some joined tables (and there are multiple calls to fnDecode() in the queries I'm porting)
We're using SQL Server 2012 and 2014.

Porting procedural approaches "as it is" is not a good practice at all. This is not a PHP, C# or even PL/SQL. Furthermore, in the case described you don't even need any function calls. NULL and empty string handling in scenarios mentioned above is very simple and does not require any procedural methods.
There are functions NULLIF, ISNULL, COALESCE, CASE statement. Everything you need is to check if something is true or false, null or not.Scalar UDF calls for every row may (and will) result in serious performance issues.Check this out:
CASE pcr.person_type
WHEN 'N' THEN npcr.first_name
+ IsNull(' ' + npcr.middle_name, '')
+ ' '
+ npcr.last_name
WHEN 'L' THEN lpcr.name
END
even if you have disabled ANSI_NULLS and CONCAT_NULL_YIELD_NULL options and dont' wish to give any chance to server to do anything wrong you can use case and this approach will be much better than calling a function:
CASE pcr.person_type
WHEN 'N' THEN npcr.first_name
+ case when npcr.middle_name is NULL then '' else ' ' + npcr.middle_name end
+ ' '
+ npcr.last_name
WHEN 'L' THEN lpcr.name
END
if there are empty stings allowed this code can work well (with regular null option set):
CASE pcr.person_type
WHEN 'N' THEN npcr.first_name
+ IsNull(' ' + NullIf(npcr.middle_name, ''), '')
+ IsNull(' ' + NullIf(npcr.last_name, ''), '')
WHEN 'L' THEN lpcr.name
END

Yes, it's certainly possible to call scalar functions from within CASE expressions (and many other places).
The issue you're experiencing relates to the first parameter. BIT is a numeric data type. Not a boolean and, in fact, SQL Server does not have an exposed boolean data type in T-SQL. So you cannot make the first parameter of your fDecode function a boolean, nor can you pass the result of evaluating a predicate, such as npcr.middle_name = NULL in a place that expects to accept a bit.
Something like this should execute:
CASE pcr.person_type
WHEN 'N' THEN npcr.first_name
+ dbo.fnDecode(CASE WHEN npcr.middle_name = NULL THEN 1 ELSE 0 END, '', ' ' + npcr.middle_name)
+ ' '
+ npcr.last_name
WHEN 'L' THEN lpcr.name
END
(Although you may also need to insert an explicit cast also)
But this makes the cure look worse than the curse.
In any case, you can just use the built in IIF function rather than fDecode. Because it's built in, it doesn't play by the same rules that you have to play by, and in fact its first parameter is a boolean expression.

Related

Removing specified character from string in SQL

Say I have a string looking like this ",LI,PA,LK";
I want to remove the first char, so it looks like "LI,PA,LK";
In Java my code to handle this, will look like this:
public String returnSubs(String val) {
int index = val.indexOf(",");
String res = val.substring(index+1, val.length());
return res;
}
I want to achieve the exact same thing in SQL, having this query
select patientID, case when liver is not null then 'LI' else '' end
|| case when kidney_r is not null then ',KR' else '' end
|| case when kidney_l is not null then ',KL' else ''end
|| case when heart is not null then ',HE' else '' end
|| case when liver_domino is not null then ',LI-Dom' else '' end
|| case when lung_r is not null then ',LungR' else '' end
|| case when pancreas is not null then ',PA' else '' end
|| case when liver_split is not null then ',Lsplit' else '' end
|| case when lung_l is not null then ',LungL' else '' end
|| case when intestine is not null then ',Intestine' else '' end
into organType
from offers
where patientID > 1
;
Also, the string I get from the query above, could look like LI, PA, KL, (notice the comma is at the end, and not the begining)
I see that I can use the SUBSTRING and/or INSTR of SQL. But I'm not really sure how. I am creating a procedure where this will be handled
Thanks for any help
Oracle has a function trim() that does exactly what you want:
trim(leading ',' from col)
You can use this in either an update or select.
Note: You appear to be storing multiple values in a comma-delimited list. That is a very bad way to model data. You do not want to overload what strings are by storing multiple values. Oracle has many better alternatives -- association tables, nested tables, JSON, and XML come to mind.
You could also use LTRIM here:
SELECT
LTRIM(organTypes, ',') AS col_out
FROM offers;
Some databases, such as MySQL, offer functions like CONCAT_WS which concatenate with a separator while ensuring that no dangling separators are added to the resulting output. Oracle does not have this, but LTRIM should be sufficient here.
even this will work:
substr(',LI,PA,LK',2)
In SQL SERVER:
SUBSTRING(VAL,2,LEN(VAL))
VAL--> COLUMN NAME
2--> IT SKIPS 1ST VALUE
LEN-->LENGTH OF THE COLUMN

how to concatenate Where clause with Like

I'm trying to concatenate SET #WhereClause= #WhereClause + 'con.BusinessName LIKE ' + '%'+CONVERT(VARCHAR,#BusinessName) + '%' + ' AND '; this line.
I want to result like this SET #WhereClause= con.BusinessName LIKE '%Hello%' AND ';
You should really be using parameters. But you need the single quotes:
'''%'+CONVERT(VARCHAR(255), #BusinessName) + '%'''
You should also specify the length for VARCHAR() in SQL Server. The default varies by context and debugging problems can be quite hard.
Note: This will fail if #BusinessName has a single quote. Think: parameters.
Also, you could use QUOTENAME function (SQL Server 2008 and above):
SET #WhereClause = #WhereClause + 'con.BusinessName LIKE ' + QUOTENAME('%' + CONVERT(varchar(126), #BusinessName) + '%', '''') + ' AND ';
With the QUOTENAME function you can avoid your code to break when #BusinessName contains single quote characters, and eliminate the possibility of SQL injection attacks as well (for example, if #BusinessName gets its value directly from an UI, via a query parametar or request body in a web application/APIs, etc.).
The QUOTENAME is intended for making valid delimited identifiers in dynamic SQL, but it could be used in your case.
The function accepts nvarchar(128) as an input string, and if the #BusinessName is longer than 126 characters (128 minus two % characters) it will return NULL. So, you should take care of that edge case.

Convert search from SQL Server to MySQL

i need to convert this one from SQL Server into MySQL
IF IsNull(#SearchText, '') <> '' BEGIN
SET #SearchText = '%' + #SearchText + '%'
SELECT NewsID,DeptID,DeptName,Title,Details ,NewsDate,img
FROM #tbSearchtextTb
WHERE IsNull(Title,'')+IsNull(Details,'') LIKE #SearchText END
this code will search fro my search word in this columns: Title, Details.
i tried to convert this line but i had lots of errors:
these are my unsuccessful attempts
IF ISNULL(SearchText,'') <> '' THEN
SELECT CatID,CatTitle,CatDescription,CatTitleAr,CatDescriptionAr,PictureID,Published,DisplayOrder,CreatedOn
FROM tmp
WHERE CatTitle + CatDescription + CatTitleAr + CatDescriptionAr
LIKE $SearchText;
and this one
IF $SearchText IS NOT NULL THEN
SELECT CatID,CatTitle,CatDescription,CatTitleAr,CatDescriptionAr,PictureID,Published,DisplayOrder,CreatedOn
FROM tmp
WHERE ISNULL(CatTitle,'') +ISNULL(CatDescription ,'') +ISNULL(CatTitleAr ,'') +ISNULL(CatDescriptionAr,'') LIKE $SearchText;
and many many other ways but i could not find any.
so if you know please let me know, thanks and best regards.
you can use fulltext search in mysql
http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html
The SQL Server version can also be written:
IF #SearchText <> '' -- null is implicitly <> ''
BEGIN
SELECT NewsID,DeptID,DeptName,Title,Details ,NewsDate,img
FROM #tbSearchtextTb
WHERE IsNull(Title,'')+IsNull(Details,'') LIKE '%' + #SearchText + '%'
END
IsNull is IFNull in MySQL.
Strings are joined using Concat in MySQL
Nulls are implicitly '' in concat
IF SearchText <> '' THEN # << again, this test is sufficient
SELECT
CatID,CatTitle,CatDescription,CatTitleAr,CatDescriptionAr,
PictureID,Published,DisplayOrder,CreatedOn
FROM tmp
WHERE Concat(CatTitle, CatDescription, CatTitleAr, CatDescriptionAr)
LIKE Concat('%',$SearchText,'%');

TSQL CASE LTRIM (RTRIM NULL

SQL Syntax is still something I am learning. I am getting the error noted below the this snippet of code.
SELECT
CASE WHEN LTRIM(RTRIM(cLehmanNo)) =' ' THEN NULL
WHEN cLehmanNo IS NOT NULL THEN REPLACE ( cLehmanNo,SUBSTRING (cLehmanNo,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cLehmanNo),1), ' ' )
END asLOAN_NUMBER
,CASE WHEN LTRIM(RTRIM(cMERS)) =' ' THEN NULL
WHEN cMERS IS NOT NULL THEN REPLACE ( cMERS,SUBSTRING (cMERS,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cMERS),1), ' ' )
END asMERS_ID
and 100+ more of same.
Msg 8133, Level 16, State 1, Line 1
None of the result expressions in a CASE specification can be NULL.
What am I doing wrong? How do I keep the gist of the statement and not get this crazy error?
This happens when it can't infer the type.
e.g.
SELECT CASE WHEN 1 = 2 THEN NULL ELSE NULL END
But this works
SELECT CASE WHEN 1 = 2 THEN NULL ELSE replace(NULL,'','') END
so I doubt the error is from the code you have shown us (You are using string functions and the following quick test shows that it will assume that to be varchar(8000))
SELECT CASE WHEN 1 = 2 THEN NULL ELSE REPLACE(NULL,'','') END a
INTO t /*Creates column of datatype varchar(8000)*/
You need to convert NULL to a correct type matching the overall values, e.g. CONVERT(VARCHAR(10), NULL), otherwise the server can't deduce which type to make the resulting value.
The error message actually means that all results in one of your case expressions are null. You have an expression like:
case when something then null when something then null end
At least one of the results has to be something other than null. You could circumvent this, but most likely there is a mistake in the query, as a case exression that always returns the same result is pointless.
The error message has been changed to:
At least one of the result expressions
in a CASE specification must be an
expression other than the NULL
constant.
SELECT
CASE WHEN LTRIM(RTRIM(cLehmanNo)) =' ' THEN NULL
WHEN cLehmanNo IS NOT NULL THEN REPLACE ( cLehmanNo,SUBSTRING (cLehmanNo,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cLehmanNo),1), ' ' )
ELSE ''
END asLOAN_NUMBER
,CASE WHEN LTRIM(RTRIM(cMERS)) =' ' THEN NULL
WHEN cMERS IS NOT NULL THEN REPLACE ( cMERS,SUBSTRING (cMERS,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cMERS),1), ' ' )
ELSE ''
END asMERS_ID

Issue With Apostrophe

I have a Proc that was coded in Dynamic SQl for one of my Application. It is using to search the Applicants with their last name. Right now it is searching applicants with either their first 2 digits of their last name or full last name. But i have a problem searching Applicants that have Apostrophe in their last name(Example O'Connor). If the client try to search applicant with O' or O'Connor it is throwing an error. They want to search every Applicant with or without Apostrophe in their last name. Please Help I tried everything, but its not working. Below is my search code that using in the Proc to pull applicants:
Add wildcards if necessary
if Rtrim(#FirstName) <> ''
begin
If(Len(#FirstName) < 30) and (CharIndex('%', #FirstName) = 0) and #FirstName != ''
Set #FirstName = char(39) + #FirstName + '%' + char(39)
end
if Rtrim(#LastName) <> ''
begin
If(Len(#LastName) < 60) and (CharIndex('%', #LastName) = 0) and #LastName != ''
Set #LastName = Char(39) + #LastName + '%' + char(39)
end
Now build dinamically the filter base on input parameters
if Rtrim(#LastName) <> ''
select #Where = #Where + ' and a.LastName like '+ Rtrim(#LastName)
You need to escape the apostrophe in the input string (basically replace a single ' with two '') as you build your SQL string
You need to pay attention to this anywhere you choose to pass user input to a SQL server database as its a security issue (SQL Injection Attacks) c.f. Bobby Tables
if Rtrim(#LastName) <> ''
select #Where = #Where + ' and a.LastName like '+ Replace(Rtrim(#LastName),'''','''''') + ''
My suggestion is to write the query to contain an alternate column to be used for filtering with the apostrophe/single quote replaced by any special character such as a #. This allows you to leave the original column intact in case you want to display it.
To do that in SQL Server, you could do something like this:
Select
tbl.strName_Last,
REPLACE(tblOrder.strName_Last, '''','#')) as StrLastNameForFilter
....
Then change your code to filter based on this alternate column, and in the user-provided filter string, replace the apostrophe/single quote with the special character.