Concatenate string using IF statement - tsql

Sorry if this is a duplicate, I feel like this has had to have been asked before, but I might just not know how to word to search correctly.
So, in my stored I take in some information and I want to create a varchar string based on this information. Lets say I have these three variables.
#String varchar(MAX) = '',
#BroughtInInfo bit
And now I have something like the following
SET #String = 'Here is a string and I want to add'
IF #BroughtInInfo = 1
BEGIN
+'This info'+
END
ELSE
+'That info'+
'Then more stuff here after conditional statement'
Now, I'm getting syntax error near + I've tried a lot of combinations of moving the plus signs around the conditional statement but it doesn't play nice.
I'm pretty new to SQL so any tips and tricks will help! Thanks!

The expression you are looking for is CASE (MSDN).
SET #String = 'Here is a string and I want to add' +
CASE WHEN #BroughtInInfo = 1
THEN 'This info'
ELSE 'That info'
END
+ 'Then more stuff here after conditional statement'

Try This on SQLServer 2008 it will work
declare #String varchar(max);
declare #BroughtInInfo bit;
SET #String = 'Here is a string and I want to add'
IF #BroughtInInfo = 1
BEGIN
SET #String +='This info'
END
ELSE
SET #String +='This info' select #String

Related

T-SQL initial capitalization - leave 2-n chars alone

I have a t-sql function I found online, maybe on stack, that works perfectly for camel-casing all words in a string:
CREATE FUNCTION InitialCap(#String VARCHAR(75))
RETURNS VARCHAR(75)
AS
BEGIN
DECLARE #Position INT;
SELECT #String = STUFF(LOWER(#String),1,1,UPPER(LEFT(#String,1))) , #Position = PATINDEX('%[^A-Za-z''][a-z]%',#String);
WHILE #Position > 0
SELECT #String = STUFF(#String,#Position,2,UPPER(SUBSTRING(#String,#Position,2))) COLLATE Latin1_General_Bin,
#Position = PATINDEX('%[^A-Za-z''][a-z]%',#String COLLATE Latin1_General_Bin);
RETURN ISNULL(#String,'');
END
The problem is that I now need a slight mod of this, such that it leaves alone all but the first letter of each word. I still want it to capitalize the first letter of each word, but then I don't want it to ever change other letters in that word. So here are some examples of my desired outcome:
'here is foo' -> 'Here Is Foo'
'i like the FBI' -> 'I Like The FBI'
For the 2nd example, note that my function shown above changes "FBI" to "Fbi", and that's the problem. I'm not sure how to change the sql to achieve it so that it would leave FBI as FBI. Now of course if the original string were "fbi", I know it would change it to "Fbi", and that's fine.
In summary, I never want the function to change an upper case letter to lower case.
Just get rid of LOWER() in STUFF():
CREATE FUNCTION InitialCap(#String VARCHAR(75))
RETURNS VARCHAR(75)
AS
BEGIN
DECLARE #Position INT;
SELECT #String = STUFF(#String,1,1,UPPER(LEFT(#String,1))) , #Position = PATINDEX('%[^A-Za-z''][a-z]%',#String);
WHILE #Position > 0
SELECT #String = STUFF(#String,#Position,2,UPPER(SUBSTRING(#String,#Position,2))) COLLATE Latin1_General_Bin,
#Position = PATINDEX('%[^A-Za-z''][a-z]%',#String COLLATE Latin1_General_Bin);
RETURN ISNULL(#String,'');
END
GO
DECLARE #String VARCHAR(75) = 'i like the FBI'
SELECT dbo.InitialCap(#String)
Results:
I Like The FBI

comma separated value in sql statement from a variable

I am getting comma separated value like this in a variable (let say variable name #listobj)
'abc' , 'xyz'
but when I am using below statement it is not giving me the correct result
SELECT * FROM someTable
Where column1 IN (#listobj)
but abc is present in the table.
Where I am doing it wrong?
create a function that split the string to
CREATE FUNCTION dbo.Split(#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
then make call to the function SELECT * FROM someTable
Where column1 IN (dbo.Split(#listobj))
enter link description here
SQLFiddle demo
select * from someTable
where ','+LTRIM(RTRIM(#listobj))+',' LIKE '%,'+LTRIM(RTRIM(column1))+',%'
A classic question and the answer is no, you cannot use a parameter in this way. There are several workarounds though
One of which is to parse the value inside the stored procedure and dynamically generate sql inside the procedure to be execute later. However, this is not a good practice.
Refer to this question
How to pass a comma separated list to a stored procedure?
and also some good discussion on it here
http://social.msdn.microsoft.com/Forums/en-US/sqlintegrationservices/thread/1ccdd39e-8d58-45b2-9c21-5c4dbd857f95/

Removing space sql server 2008

I am trying to write a stored procedure in sql server 2008,I need to remove unwanted spaces in the entries of my table.I categorized the entries in my table to 3 types.My store procedure should remove the spaces around single letter,like,
A G M words to AGM words
words A G M words to words AGM words
A G words to AG words
I tried following stored procedure.
CREATE proc At1 #name nvarchar(100)
as
declare #start int
declare #temp1 nvarchar(100)
declare #temp nvarchar(100)
declare #NthPosition int
declare #N int
set #N=LEN(#name)
set #start=1
set #temp1=''
set #temp=''
set #NthPosition=charindex(' ',#name,#start)
if(#NthPosition<>0)
begin
while (#NthPosition<>0 and #N<>0)
begin
set #temp1=SUBSTRING(#name,#start,#NthPosition-1)
if(#temp<>'')
begin
if(len(#temp1)=1)
begin
set #temp=(#temp+#temp1)
end
else
begin
set #temp=(#temp+' '+#temp1)
end
end
else
begin
set #temp=#temp1
end
set #start=#NthPosition+1
set #N=#N-#NthPosition
set #NthPosition=0
set #NthPosition=CHARINDEX(' ',#name,#start)
end
end
else
begin
select #name
end
select #temp
GO
and i used ,
exec At1 'apple A G M mango'
My expected result: apple AGM mango
But my actual result:apple
I am unable to figure out where the error is..Any suggestions in this regard is more helpful.
I tried to use computed column that would clear the space and i was able to find solution only for pattern #3.I am unable to frame a computed column definition suitable for all the 3 patterns..... Please share your thoughts that will be helpful to me
I think this covers all the cases:
CREATE proc At1 #Name nvarchar(100)
as
declare #New nvarchar(100)
declare #SpacePos int
declare #Single bit
select #New = '',#Single = 0
select #Name = LTRIM(#Name)
while LEN(#name) > 0
begin
set #SpacePos = CHARINDEX(' ',#Name)
if #SpacePos = 0 --No more spaces in the string
begin
select #New = #New + CASE WHEN #Single = 1 and LEN(#Name) > 1 THEN ' ' ELSE '' END + #Name,
#Name = ''
end
else if #SpacePos = 2 --Single character "word"
begin
select #New = #New + SUBSTRING(#Name,1,1),
#Name = SUBSTRING(#Name,3,100),
#Single = 1
end
else --Multi-character word
begin
select #New = #New + CASE WHEN #Single = 1 THEN ' ' ELSE '' END + SUBSTRING(#Name,1,#SpacePos),
#Name = SUBSTRING(#Name,#SpacePos+1,100),
#Single = 0
end
end
select #New
go
And the examples:
exec At1 'apple A G M mango'
exec At1 'A G M words'
exec At1 'words A G M'
Produces:
apple AGM mango
AGM words
words AGM
(As a simplifying assumption, I assumed I was okay to remove any leading spaces from the original string. I also assume there are no double spaces in the string. If neither of those assumptions is accurate, a bit more work is required)
There might be a little more simpler approach to this, use replace instead of looping through everything and use the substring method.
But then again, you also might look at your input. How does this "processor" knows what a word is? For a matter of fact, the word applea (apple a) might not be a word you are looking for, where this processor potentially will see it as a word (theoretical)
The best thing you can do is to separate your input, for example with an semicolon ";". Then you can use a split functionallity to make those values into a table (for example look at this post : T-SQL: split and aggregate comma-separated values). Next you can use the replace function on it.
You get something like this
select replace(s.value, ' ' , ''), * from split(#value) as s

Parameterised stored proc

I'm writing a parameterized stored proc. I know that you can set the parameter value such that it displays all the results when the parameter is not mentioned in the execute command.. But i'm unable to recall how that is achieved. Any help is highly appreciated... Please..
I'd recommend parameterized dynamic sql (sp_executesql)
Going this route, you can discard any irrelevant parameter when building your where clause.
Example procedure:
create proc dbo.SearchForStuff
(
#Id int = 0
,#Description varchar(100) = ''
)
as
begin
set nocount on;
declare #select nvarchar(max) = '
select
s.*
from Stuff as s'
declare #where varchar(max) = ''
if isnull(#ID,0) != 0 begin
set #where += case #where when '' then ' where ' else ' and ' end + 's.Id = #Id'
end
if isnull(#Description,'') != '' begin
set #where += case #where when '' then ' where ' else ' and ' end + 's.[Description] = #Description'
end
set #select += #where
exec sp_executesql
#select
,N'
,#Id int = 0
,#Description varchar(100) = '''''
,#Id
,#Description
end
Usage:
exec SearchForStuff #Id = 1, #Description = 'omg' -- Returns every item where Id is 1 and Description is 'omg'
exec SearchForStuff #Id = 1 -- Returns every item where Id is 1
exec SearchForStuff #Description = 'omg' -- Returns every item where Description is 'omg'
exec SearchForStuff --returns every item
In this fashion your final query is not littered with useless conditions. Further, you can get a bit more granular than I did here. Based upon which parameters were passed, you can tailor your where/join clauses to take advantage of your indexes such that you get the best possible performance. The only drawback is a slight loss of readability (imo).
You can make your WHERE conditions like this:
WHERE (#myParam IS NULL OR #myParam = someValue)
You may be able to use OPTION (RECOMPILE) is SQL2008SP1+ (or similar, don't know other options) in the sproc, depending on your RDBMS, to get this to be performant.
Method from Erland Sommarskog:
http://www.sommarskog.se/dyn-search-2008.html#static
From the link:
"The effect of all the #x IS NULL clauses is that if that input parameter is NULL, then that AND-condition is always true. Thus, the only conditions that are in effect are those where the search parameter has a non-NULL value.
As far as maintainability goes, it's difficult to think of a better solution for the search conditions at hand. It's compact, easy to read and to extend. And performance? Very good as long as you include the query hint OPTION (RECOMPILE). This hint forces the query to be recompiled each time, in which case SQL Server will use the actual variable values as if they were constants."
If it is an int you can use
SELECT X,Y
FROM T
WHERE C BETWEEN COALESCE(#P, -2147483648) AND COALESCE(#P, 2147483647)
The definitive article on the subject

Try-Catch in User Defined Function?

I'm trying to write a UDF to translate a string that is either a guid or a project code associated with that guid into the guid:
CREATE FUNCTION fn_user_GetProjectID
(
#Project nvarchar(50)
)
RETURNS uniqueidentifier
AS
BEGIN
declare #ProjectID uniqueidentifier
BEGIN TRY
set #ProjectID = cast(#Project as uniqueidentifier)
END TRY
BEGIN CATCH
set #ProjectID = null
END CATCH
if(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project
END
return #ProjectID
END
This works fine if the above code is embedded in my Stored Procedures, but I'd like to make a function out of it so that I follow DRY.
When I try to create the Function, I get errors like this:
Msg 443, Level 16, State 14, Procedure fn_user_GetProjectID, Line 16
Invalid use of side-effecting or time-dependent operator in 'BEGIN TRY' within a function.
Does anyone have an idea how I can get around this error?
Edit: I know I can't use Try-Catch in a Function, I guess a simplified questions would be, is there a way to do a cast that will just return NULL if the cast fails, instead of an error?
Apparently you can't use TRY-CATCH in a UDF.
According to this bug-reporting page for SQL Server:
Books Online documents this behaviour,
in topic "CREATE FUNCTION
(Transact-SQL)": "The following
statements are valid in a function:
[...] Control-of-Flow statements
except TRY...CATCH statements. [...]"
But they were giving hope for the future back in 2006:
However, this is a severe limitation
that should be removed in a future
release. You should post a suggestion
in this regard and I will
wholeheartedly vote for it.
From MSDN:
A column or local variable of
uniqueidentifier data type can be
initialized to a value in the
following ways:
By using the NEWID function.
By converting from a string constant
in the form
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
in which each x is a hexadecimal digit
in the range 0-9 or a-f.
For example,
6F9619FF-8B86-D011-B42D-00C04FC964FF
is a valid uniqueidentifier value.
You can use pattern matching to verify the string. Note that this won't work for specific encoding that reduces the size of the GUID:
declare #Project nvarchar(50)
declare #ProjectID uniqueidentifier
declare #HexPattern nvarchar(268)
set #HexPattern =
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]'
/* Take into account GUID can have curly-brackets or be missing dashes */
/* Note: this will not work for GUIDs that have been specially encoded */
set #Project = '{' + CAST(NEWID() AS VARCHAR(36)) + '}'
select #Project
set #Project = REPLACE(REPLACE(REPLACE(#Project,'{',''),'}',''),'-','')
/* Cast as uniqueid if pattern matches, otherwise return null */
if #Project LIKE #HexPattern
select #ProjectID = CAST(
SUBSTRING(#Project,1,8) + '-' +
SUBSTRING(#Project,9,4) + '-' +
SUBSTRING(#Project,13,4) + '-' +
SUBSTRING(#Project,17,4) + '-' +
SUBSTRING(#Project,21,LEN(#Project)-20)
AS uniqueidentifier)
select #ProjectID
I know I can't use Try-Catch in a Function, I guess a simplified questions would be, is there a way to do a cast that will just return NULL if the cast fails, instead of an error?
Starting from SQL Server 2012 you could use TRY_CAST/TRY_CONVERT functions:
Returns a value cast to the specified data type if the cast succeeds; otherwise, returns null.
CREATE FUNCTION fn_user_GetProjectID(#Project nvarchar(50))
RETURNS uniqueidentifier
AS
BEGIN
declare #ProjectID uniqueidentifier = TRY_CAST(#Project as uniqueidentifier);
IF(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project;
END
return #ProjectID;
END
Not sure, but why not flip it around... at first glance I would simplify it like this:
select #ProjectID =
ISNULL((select ProjectID from Project where
projectcode = #Project)
,(cast #Project as uniqueidentifier))
If this doesn't provide enough error handling, I'm sure there's a better way to pre-check that the cast can work without using try/catch...
My brute force method was to create my own ToGuid() function that verifies it can be converted to a GUID first, if not, it returns null. It may not be very fast but it does the job, and it is probably faster to convert the guid if it is one than to try to look it up in the table. EDIT: I meant to give credit to this blog, where I got the basis of my code for this function: http://jesschadwick.blogspot.com/2007/11/safe-handling-of-uniqueidentifier-in.html
CREATE FUNCTION [dbo].[ToGuid]
(
#input NVARCHAR(MAX)
)
RETURNS uniqueidentifier
AS
BEGIN
DECLARE #isValidGuid BIT;
DECLARE #temp NVARCHAR(MAX);
SET #isValidGuid = 1;
SET #temp = UPPER(LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(#input, '-', ''), '{', ''), '}', ''))));
IF(#temp IS NOT NULL AND LEN(#temp) = 32)
BEGIN
DECLARE #index INT;
SET #index = 1
WHILE (#index <= 32)
BEGIN
IF (SUBSTRING(#temp, #index, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F'))
BEGIN
SET #index = #index + 1
END
ELSE
BEGIN
SET #isValidGuid = 0
BREAK;
END
END
END
ELSE
BEGIN
SET #isValidGuid = 0
END
DECLARE #ret UNIQUEIDENTIFIER
IF(#isValidGuid = 1)
set #ret = cast(#input AS UNIQUEIDENTIFIER)
ELSE
set #ret = NULL
RETURN #ret
END
I'm still very interested if there is a better answer than this.
Verify if #Project is a number using the ISNUMERIC function.
your code should looks like that:
declare #ProjectID uniqueidentifier
set #ProjectID = null
IF ISNUMERIC(#Project) > 0
BEGIN
set #ProjectID = cast(#Project as uniqueidentifier)
END
if(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project
END
return #ProjectID