I tried this code -
UPDATE Table
SET Name = RTRIM(LTRIM(Name))
Data type of Name is varchar(25)
None of the leading and trailing spaces get removed. When I copy-paste one such Name,
i get this -
"big dash" "space symbol" ABC001
Why is this happening and how do trim the spaces ?
EDIT -
The question has already been answered. I found one more table with this problem. I get
"- value" when i copy a column of a row. When I press the enter key at end of this copy-pasted value, i see more dashes. See image below -
Kindly use below query it will remove space new line etc..
select LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(Name, CHAR(10), CHAR(32)),CHAR(13), CHAR(32)),CHAR(160), CHAR(32)),CHAR(9),CHAR(32))))
I suspect, some non readable(Non-ascii characters) inside the name column, that might not get removed as part of TRIM calls.
select convert(varbinary, Name) from table
Reading the HEX output from above query should reveal the same.
Kindly read this to find how to write functions to remove such characters.
You could do something brute force, such as removing the first character "manually" if it is not alphanumeric:
update table
set name = rtrim(ltrim(case when name not like '[a-zA-Z0-9]%'
then stuff(name, 1, 1, '')
else name
end)
);
You could also search and replace that particular character:
update table
set name = rtrim(ltrim(replace(name, "big dash", '')));
It is a frequent occurrence that we must remove leading and trailing whitespaces from a string before additional processing or sending it to another layer in an application. We can’t always control how the data is entered. The data might come from another system, a data conversion, an old application, EDI, Excel, or from an application which had poor quality control. In some of those cases, a whitespace might not be entered or saved in the system as character 32 which is a whitespace entered in a keyboard. If that happens, SQL built in functions for trimming whitespaces do not work so it becomes necessary to replace the “other” whitespace characters with character 32. Then LTRIM and RTRIM will work as expected.
**Select [udfTrim](ColumnName) from Table**
**CREATE FUNCTION [dbo].[udfTrim]
(
#StringToClean as varchar(8000)
)**
RETURNS varchar(8000)
AS
BEGIN
--Replace all non printing whitespace characers with Characer 32 whitespace
--NULL
Set #StringToClean = Replace(#StringToClean,CHAR(0),CHAR(32));
--Horizontal Tab
Set #StringToClean = Replace(#StringToClean,CHAR(9),CHAR(32));
--Line Feed
Set #StringToClean = Replace(#StringToClean,CHAR(10),CHAR(32));
--Vertical Tab
Set #StringToClean = Replace(#StringToClean,CHAR(11),CHAR(32));
--Form Feed
Set #StringToClean = Replace(#StringToClean,CHAR(12),CHAR(32));
--Carriage Return
Set #StringToClean = Replace(#StringToClean,CHAR(13),CHAR(32));
--Column Break
Set #StringToClean = Replace(#StringToClean,CHAR(14),CHAR(32));
--Non-breaking space
Set #StringToClean = Replace(#StringToClean,CHAR(160),CHAR(32));
Set #StringToClean = LTRIM(RTRIM(#StringToClean));
Return #StringToClean
END
If your string has some non-unicode chars, then those need to be removed first. The functions for that are given later, taken from this link - http://iso30-sql.blogspot.com/2010/10/remove-non-printable-unicode-characters.html
First, check if there are any weird hex chars using -
select convert(varbinary, Name) from table
Then, use the code given in the link above. Note that in the usage of functions, square brackets are to be removed, otherwise the code won't work. Eg. [#DatabaseName = 'MyDatabaseName',] [#SchemaName = 'MySchemaName',]
After this, your strings might have some spaces which can be removed using -
UPDATE Table
SET Name = RTRIM(LTRIM(Name))
Also NOTE that the scripts given in the above link/below will not work on the
following table -
CREATE TABLE [dbo].[Junk](
[JunkHex] nvarchar(50) NULL
) ON [PRIMARY]
GO
GO
INSERT [dbo].[Junk] ([JunkHex]) VALUES (N'Stringğ ')
INSERT [dbo].[Junk] ([JunkHex]) VALUES (N'withħ')
INSERT [dbo].[Junk] ([JunkHex]) VALUES (N'įņvalidđ')
INSERT [dbo].[Junk] ([JunkHex]) VALUES (N'charactersŝ')
This is the content of the link I have given above -
Remove non-printable / Unicode characters in SQL Server 2005
A few months ago, I was upgrading some report templates from the older version of Excel (.xls) to Excel 2007 (.xlsx). I ran into numerous problems almost immediately when I attempted to generate the upgraded reports because the incoming data was riddled with charaters that don't play nicely with XML. The data is used for a variety of reporting purposes, so I decided to tackle the problem on the back-end by removing all but the printable ascii characters.
I started by writing a simple user function for individual strings, but I got to thinking that I may want to automate some of these cleanup tasks and ended up putting something together that allows for a bit more the flexibility. The following creates the basic string user function, along with two procedures to perform the cleanup at the column and table level:
Note - Each of the scripts below uses all the ones above it. So, execute all scripts in order to get all functionality.
Function: fn_npclean_string
use [master]
go
set ansi_nulls on
go
set quoted_identifier on
go
CREATE function [dbo].[fn_npclean_string] (
#strIn as varchar(1000)
)
returns varchar(1000)
as
begin
declare #iPtr as int
set #iPtr = patindex('%[^ -~0-9A-Z]%', #strIn COLLATE LATIN1_GENERAL_BIN)
while #iPtr > 0 begin
set #strIn = replace(#strIn COLLATE LATIN1_GENERAL_BIN, substring(#strIn, #iPtr, 1), '')
set #iPtr = patindex('%[^ -~0-9A-Z]%', #strIn COLLATE LATIN1_GENERAL_BIN)
end
return #strIn
end
Procedure: sp_npclean_col
use [master]
go
set ansi_nulls on
go
set quoted_identifier on
go
CREATE procedure [dbo].[sp_npclean_col]
#DatabaseName varchar(75) = null,
#SchemaName varchar(75) = null,
#TableName varchar(75),
#ColumnName varchar(75)
as
begin
Declare #FullTableName varchar(100)
declare #UpdateSQL nvarchar(1000)
if #DatabaseName is null begin
set #DatabaseName = db_name()
end
if #SchemaName is null begin
set #SchemaName = schema_name()
end
set #FullTableName = '[' + #DatabaseName + '].[' + #SchemaName + '].[' + #TableName + ']'
set #UpdateSQL = 'update ' + #FullTableName + ' set [' + #ColumnName + '] = dbo.fn_npclean_string([' + #ColumnName + ']) where [' + #ColumnName + '] like ''%[^ -~0-9A-Z]%'''
exec sp_ExecuteSQL #UpdateSQL
end
Procedure: sp_npclean_table
use [master]
go
set ansi_nulls on
go
set quoted_identifier on
go
create procedure [dbo].[sp_npclean_table]
#TargetDatabase varchar(75) = null,
#TargetSchema varchar(75) = null,
#TargetTable varchar(75)
as
begin
declare #getColSQL nvarchar(750)
declare #textCol CURSOR
declare #curCol varchar(75)
if #TargetDatabase is null begin
set #TargetDatabase = db_name()
end
if #TargetSchema is null begin
set #TargetSchema = schema_name()
end
set #getColSQL =
'select sc.name
from ' + #TargetDatabase + '.sys.columns sc
join ' + #TargetDatabase + '.sys.types st
on sc.system_type_id = st.system_type_id
join ' + #TargetDatabase + '.sys.objects so
on sc.object_id = so.object_id
join ' + #TargetDatabase + '.sys.schemas ss
on so.schema_id = ss.schema_id
where
so.type = ''U''
and st.name in (''text'',''ntext'',''varchar'',''char'',''nvarchar'',''nchar'')
and sc.is_rowguidcol = 0
and sc.is_identity = 0
and sc.is_computed = 0
and so.name = ''' + #TargetTable + '''
and ss.name = ''' + #TargetSchema + ''''
set #getColSQL = 'set #inCursor = cursor for ' + #getColSQL + ' open #incursor'
execute sp_executesql #getColSQL,N'#inCursor cursor out',#inCursor=#textCol OUT
fetch next from #textCol into #curCol
while ##fetch_status = 0
begin
exec sp_npclean_col #DatabaseName = #TargetDatabase, #SchemaName = #TargetSchema, #TableName = #TargetTable, #ColumnName = #curCol
fetch next from #textCol into #curCol
end
Close #textCol
DeAllocate #textCol
end
Using these, invalid characters can be removed in the following ways:
By String:
select master.dbo.fn_npclean_string('Stringğ withħ įņvalidđ charactersŝ')
By table column:
exec master.dbo.sp_npclean_col [#DatabaseName = 'MyDatabaseName',] [#SchemaName = 'MySchemaName',] #TableName = 'MyTableName', #ColumnName = 'MyColumnName'
By table:
exec master.dbo.sp_npclean_table [#TargetDatabase = 'MyDatabaseName',] [#TargetSchema = 'MySchemaName',] #TargetTable = 'MyTableName'
I use this command while updating all rows' value.
It'll really work out.
For your example:
UPDATE Table SET Name = LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(Name, CHAR(10), CHAR(32)), CHAR(13), CHAR(32)), CHAR(160), CHAR(32)),CHAR(9),CHAR(32))))
You can use the HEX method above, or you can also use the ASCII() function to determine the ASCII code of the character in question...
SELECT ASCII(SUBSTRING(' character string', 1, 1))
SELECT ASCII(SUBSTRING(' character string', 2, 1))
The select only returns 1 value for the character you specify. But it's helpful for determining which ASCII CHAR() value(s) you need to replace.
-Eric Isaacs
Use this to identify the offending character:
select ascii(substring(' Your string with leading invisible character',1,1));
-- returns something like 160
Use this to replace the offending character
replace(' Your string with leading invisible character', char(160),'')
There are cases that the LTRIM RTRIM not doing what you want, to me, it happened because of the tab key when tab key inserted to a database we cant see it in our eyes in this cases trim function doesn't work.
Try this code
UPDATE <TablaName>
SET NAME = CAST(LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(value, CHAR(9), ''), CHAR(13), ''), CHAR(10), ''))) AS VARCHAR(50))
Related
I am using MSSQL 2016,
I need to be able to update a row on a table dynamically.
I got a stored procedure :
CREATE PROCEDURE sp_lookupData_UpdatelookupValues
(
#FullTableName nvarchar(50),
#Id nvarchar(10),
#Name nvarchar(50),
#Description nvarchar(50)
)
AS
BEGIN
DECLARE #Cmd nvarchar(150) = N'UPDATE ' + #FullTableName + ' SET Name = ' + #Name + ', Description = ' + #Description + ' WHERE ID = ' + #Id + '';
EXECUTE sp_executesql #Cmd;
END
The problem is that Name and Description values are passed into the #Cmd like this :
UPDATE TABLE_NAME SET Name = Private, Description = Default WHERE ID = 1
Instead of 'Private' and 'Default'.
The result is an error where Private is being counted as a column which doesnt exist ( because of the bad format ).
Invalid column name 'Private'.
Put the quotes yourself
Use single quotes around Private and Default.
And since you are using dynamic querying, you have to double the single quotes to escape them.
DECLARE #Cmd nvarchar(150) = N'UPDATE ' + #FullTableName + ' SET Name = ''' + #Name + ''', Description = ''' + #Description + ''' WHERE ID = ' + #Id + '';
Also make sure you try the next solution, since the first one is SQL Injection compatible.
Use sp_executesql parameters
You can also use the parameters inside your #Cmd without doing the concatenation yourself but by passing the parameters to sp_executesql
Also I suggest you to QUOTENAME the #FullTableName parameter in case of spaces inside table's name.
DECLARE #Cmd nvarchar(150) = N'UPDATE QUOTENAME(#FullTableName) SET Name = #Name, Description = #Description WHERE ID = #Id;'
EXEC sp_executesql #Cmd, #FullTableName, #Name, #Description, #Id;
The advantage doing so, is you avoid any parameters not checked by the application to be able to do SQL Injection.
Reference :
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql
You have to add the quotes yourself. I prefer to use QUOTENAME to keep all the quotes recognizable:
QUOTENAME(#FullTableName, '''')
You cannot use parameters for identifiers. But, you can use parameters for values:
DECLARE #Cmd nvarchar(150) = N'
UPDATE ' + #FullTableName + '
SET Name = #Name,
Description = #Description
WHERE ID = #Id';
EXECUTE sp_executesql #Cmd,
N'#Name nvarchar(50), #Description nvarchar(50), #Id nvarchar(10)',
#Name = #Name, #Description = #Description, #Id = #id;
Unfortunately, the dynamic table name still poses risks, both in terms of SQL injection and syntax errors. I am guessing this is "controlled" code, not code with user input, so the risks might be acceptable. However, you probably should use quotename().
That brings up another issue which is probably the crux of the problem. Why do you have multiple tables with the same columns? Could these -- should these -- all be stored in a single table? This type of code calls into question aspects of the data model.
Lets say I have a parameter #Name in a Stored Procedure. I want to filter by this parameter only if it is not empty / null. In any other case I want to ignore the filter.
I came up with the following two solutions. For the sake of example let us consider only the case that parameter is empty.
select *
from MyTable
where (len(rtrim(ltrim(#Name))) > 0 and Name = #Name) or (len(rtrim(ltrim(#Name))) = 0)
and the second one
#query = 'select * from MyTable'
if (len(rtrim(ltrim(#Name))) > 0)
#query = #query + ' Name = #Name '
Both of the approaches are working as expected.
Which do you think is the most clean (in terms of code) and easily maintainable
Are there any other (better) alternatives.
Note: This question may also suit in Code Review, please comment if you think so, in order to migrate there
It can be simplified like this
select *
from MyTable
where Name = #Name or #Name = '' or #Name is null
or as mentioned in comments, use NULLIF to check for empty string then replace it with NULL then validate it against IS NULL
where (Name = #Name or nullif(#Name, '') is null)
You don't need to check for length, by default, sql server is trailing-spaces-sensitive (The only exception to this rule is when the right side of the LIKE predicate's expression contains trailing spaces, then the pad is not removed).
Take the code below.
DECLARE #Name=' '
IF(#Name='') SELECT 1 ELSE SELECT 0
If you run the above code above you will get a result of 1. In your case, you can drop the LTRIM and RTRIM and simply test for equality against an empty string literal.
select *
from MyTable
where ((#Name='' OR #Name IS NULL)OR(Name = #Name))
OR
IF(#Name='') SET #Name=NULL
select *
from MyTable
where (#Name IS NULL OR Name = #Name)
if you are working with dynamic sql in stored procedure try something like this . It is better to use different variables for main select query and dynamic where query which can be extended easily . using this approach it will be easy to maintain when you proc becomes lengthy.
declare #finalquery varchar(max)
declare #mainSelectquery nvarchar(500);
declare #whereCondtions varchar (1000);
declare #DateParam datetime
set #mainSelectquery=''
set #whereCondtions =''
set #finalquery =''
set #DateParam=getdate()
set #mainSelectquery = 'select * from tblOrders where 1=1 '
set #whereCondtions = ' and Order_site =''TSN'''
set #whereCondtions = #whereCondtions + ' AND CAST(ORDER_APPROVED_DATE_LT AS DATE)=CAST(GETDATE() AS DATE)'
set #finalquery =( #mainSelectquery + #whereCondtions)
print #finalquery
---- You can further extend this by adding more where condition based on the parameter pass in stored proc
if (#OrderID !=0)
begin
set #whereCondtions = ' OrderID='+str ( #stateRefID )
end
Here is what i'm trying to do. I'm trying to create a stored procedure where I could just enter the name of the table, column, and column value and it will delete any records associated with that value in that table. Is there a simple way to do this? I don't know too much about SQL and still learning about it.
Here is what I have so far.
ALTER PROCEDURE [dbo].[name of stored procedure]
#TABLE_NAME varchar(50),
#COLUMN_NAME varchar(50),
#VALUE varchar(5)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #RowsDeleted int;
DECLARE #sql VARCHAR(500);
SET #sql = 'DELETE FROM (name of table).' + #TABLE_NAME + ' WHERE ' + #COLUMN_NAME + '=' + '#VALUE'
EXEC(#sql)
SET #RowsDeleted=##ROWCOUNT
END
GO
Couple issues
First, you don't need (name of table)
SET #sql = 'DELETE FROM ' + #TABLE_NAME + etc.
In general you should try to include the appropriate schema prefix
SET #sql = 'DELETE FROM dbo.' + #TABLE_NAME + etc.
And in case your table name has special characters perhaps it should be enclosed in brackets
SET #sql = 'DELETE FROM dbo.[' + #TABLE_NAME + ']' + etc.
Since #Value is a string, you must surround it with single quotes when computing the value for #SQL. To insert a single quote into a string you have to escape it by using two single quotes, like this:
SET #SQL = 'DELETE FROM dbo.[' + #TABLE_NAME + '] WHERE [' + #COLUMN_NAME + '] = '''' + #VALUE + ''''
If #VALUE itself contains a single quote, this whole thing will break, so you need to escape that as well
SET #SQL = 'DELETE FROM dbo.[' + #TABLE_NAME + '] WHERE [' + #COLUMN_NAME + '] = '''' + REPLACE(#VALUE,'''','''''') + ''''
Also, ##ROWCOUNT will not populate from EXEC. If you want to be able to read ##ROWCOUNT, use sp_ExecuteSQL instead
EXEC sp_ExecuteSql #SQL
And finally, let me editorialize for a minute--
This sort of stored procedure is not a great idea. I know it seems pretty cool because it is flexible, and that kind of thinking is usually smart when it comes to other languages, but in the database world this approach causes problems, e.g. there are security issues (e.g. injection, and the fact that you need elevated privileges to call sp_executeSql) and there issues with precompilation/performance (because the SQL isn't known ahead of time, SQL Server will need to generate a new query plan each and every time you call this) and since the caller can supply any value for table and column name you have no idea whether this delete statement will be efficient and use indexes or if it will cause a huge performance issue because the table is large and the column is not indexed.
The proper approach is to have a series of appropriate stored procedures with strongly-typed inputs that are specific to each data use case where you need to delete based on criteria. Database engineers should not be trying to make things flexible; you should be forcing people to think through what exactly they are going to need, and implement that and only that. That is the only way to ensure people are following the rules, keeping R/I intact, efficient use of indexes, etc.
Yes, this may seem like repetitive and redundant work, but c'est la vie. There are tools available to generate the code for CRUD operations if you don't like the extra typing.
In addition to some of the information John Wu provided you have to worry about data types and ##ROWCOUNT may not be accurate if there are triggers on your tables and things..... You can get around both of those issues though by casting to nvarchar() and using OUTPUT clause with a temp table to do the COUNT().
So just for fun here is a way you can do it:
CREATE PROCEDURE dbo.[ProcName]
#TableName SYSNAME
,#ColumnName SYSNAME
,#Value NVARCHAR(MAX)
,#RecordCount INT OUTPUT
AS
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = N'IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL
BEGIN
DROP TABLE #DeletedOutput
END
CREATE TABLE #DeletedOutput (
ID INT IDENTITY(1,1)
ColumnValue NVARCHAR(MAX)
)
DELETE FROM dbo.' + QUOTENAME(#TableName) + '
OUTPUT deleted.' + QUOTENAME(#ColumnName) + ' INTO #DeletedOutput (ColumnValue)
WHERE CAST(' + QUOTENAME(#ColumnName) + ' AS NVARCHAR(MAX)) = ' + CHAR(39) + #Value + CHAR(39) + '
SELECT #RecordCountOUT = COUNT(ID) FROM #DeletedOutput
IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL
BEGIN
DROP TABLE #DeletedOutput
END'
DECLARE #ParmDefinition NVARCHAR(200) = N'#RecordCountOUT INT OUTPUT'
EXECUTE sp_executesql #SQL, #ParmDefinition, #RecordCountOUT = #RecordCount OUTPUT
END
So the use of QOUTENAME will help against the injection attack but not be perfect. And I use CHAR(39) instead of the escape sequence for a single quote on value because I find it easier when string building at that point.... By using Parameter OUTPUT from sp_executesql you can still return your count.
Keep in mind just because you can do something in SQL doesn't always mean you should.
I'm quite new to SQL Server so hopefully this makes sense :)
I'm trying to declare variables to be used in an INNER JOIN.
If you take a look at my code, you'll see what I'm trying to do, without me needing to go into too much detail. Let me know if you need more info. Is that syntax possible?
EDIT: See new attempt below
--State - If suburb/postcode, could use postcode lookup
Declare #Missing as nvarchar(255),
#MissingUpdate as nvarchar(255),
#MatchA as nvarchar(255),
#MatchB as nvarchar(255),
#Reason as nvarchar(255);
Set #Missing = '[StateEXPORT]'; -- field to update
Set #MissingUpdate = '[State]'; -- field in postcode lookup to pull in
Set #MatchA = '[PostcodeEXPORT]'; -- field in master field to match with
Set #MatchB = '[Pcode]'; -- field in postcode lookup to match with
Set #Reason = 'Contactable - Needs verificiation - #MissingUpdate taken from Lookup'; -- reason here
update [BT].[dbo].[test]
set #Missing = b.#MissingUpdate,
FinalPot = #Reason
FROM [BT].[dbo].[test] a
INNER JOIN [BT].[dbo].[Postcode Lookup] b
ON a.#MatchA = b.#MatchB
where (#Missing is null or #Missing = '0') and [AddressSource] != ('Uncontactable')
GO
EDIT: SECOND ATTEMPT:
set #sql = 'update [BT].[dbo].[test] set ' + quotename(#Missing) + '= b.' + quotename(#MissingUpdate) + ', FinalPot = ' + #Reason + 'FROM [BT].[dbo].[test] a INNER JOIN [BT].[dbo].[Postcode Lookup] b ON a.' + quotename(#MatchA) + ' = b.' + quotename(#MatchB) + 'where (' + quotename(#Missing) + 'is null or' + quotename(#Missing) + ' = 0 and [AddressSource] != "(Uncontactable)"'
exec (#sql)
Thanks for your help,
Lucas
No, this syntax is not possible, at least not directly: you need to specify the column name, not a string variable that has the name.
If you wish to decide the names of columns dynamically, you could make a SQL string that represents the statement that you wish to execute, and pass that string to EXECUTE command. You have to take extra care not to put any of the user-entered data into the generated SQL string, though, to avoid SQL injection attacks.
EDIT: The reason your second attempt may be failing is that you are passing names in square brackets to quotename. You should remove brackets from your variable declarations, like this:
Set #Missing = 'StateEXPORT'; -- field to update
Set #MissingUpdate = 'State'; -- field in postcode lookup to pull in
Set #MatchA = 'PostcodeEXPORT'; -- field in master field to match with
Set #MatchB = 'Pcode'; -- field in postcode lookup to match with
You can't use variable names as column names without dynamic SQL.
An example of a dynamic SQL query:
declare #ColumnName varchar(100) = 'col1'
declare #sql varchar(max)
set #sql = 'select ' + quotename(#ColumnName) + ' from dbo.YourTable'
exec (#sql)
I have a customer table with Cust_Id, Name, City and search is based upon any or all of the above three.
Which one Should I go for ?
Dynamic SQL:
declare #str varchar(1000)
set #str = 'Select [Sno],[Cust_Id],[Name],[City],[Country],[State]
from Customer where 1 = 1'
if (#Cust_Id != '')
set #str = #str + ' and Cust_Id = ''' + #Cust_Id + ''''
if (#Name != '')
set #str = #str + ' and Name like ''' + #Name + '%'''
if (#City != '')
set #str = #str + ' and City like ''' + #City + '%'''
exec (#str)
Simple query:
select
[Sno],[Cust_Id],[Name],[City],[Country],[State]
from
Customer
where
(#Cust_Id = '' or Cust_Id = #Cust_Id) and
(#Name = '' or Name like #Name + '%') and
(#City = '' or City like #City + '%')
Which one should I prefer (1 or 2) and what are advantages?
After going through everyone's suggestion , here is what i finally got.
DECLARE #str NVARCHAR(1000)
DECLARE #ParametersDefinition NVARCHAR(500)
SET #ParametersDefinition = N'#InnerCust_Id varchar(10),
#InnerName varchar(30),#InnerCity varchar(30)'
SET #str = 'Select [Sno],[Cust_Id],[Name],[City],[Country],[State]
from Customer where 1 = 1'
IF(#Cust_Id != '')
SET #str = #str + ' and Cust_Id = #InnerCust_Id'
IF(#Name != '')
SET #str = #str + ' and Name like #InnerName'
IF(#City != '')
SET #str = #str + ' and City like #InnerCity'
-- ADD the % symbol for search based upon the LIKE keyword
SELECT #Name = #Name + '%', #City = #City+ '%'
EXEC sp_executesql #str, #ParametersDefinition,
#InnerCust_Id = #Cust_Id,
#InnerName = #Name,
#InnerCity = #City;
Note : #Cust_Id, #Name and #City are parameters being passed to the stored procedure
References :
http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/changing-exec-to-sp_executesql-doesn-t-p
http://www.sommarskog.se/dynamic_sql.html
http://msdn.microsoft.com/en-us/library/ms175170.aspx
Dynamic SQL can be a little more difficult to write, and it is vulnerable to SQL Injection if you are not careful. However, it outperforms the "non-dynamic"/Simple or query.
Read more about it here. http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/do-you-use-column-param-or-param-is-null
Dynamic SQL is likley to be more performant which is generally important in a search.
However, it is more diffiult to write and debug and test. First you need to make sure it will not allow SQL injection attacks. Next you need to make sure that the variables you use are large enough to contain the largest possible final SQl statement you would create.
Then you need to create a good number of test cases to make sure that there is not some sort of subtle bug.
You will also need to grant read permissions to the underlying tables which you normally don't need to do if you use Stored procs.
Finally when doing dynamic SQL in a stored proc, please add an input variable called #debug as the last input variable and give it a default value of 0. When a 1 is passed in, instead of executing the dynamic SQL, it will send you the SQL that is created. This will help you debug the proc and and is especially helpful when there is a an error in some future search because you can see exactly what SQL was run for those values.
From my experience, Dynamic SQL makes sense (gains performance) only of decreases the number of JOINs.
Otherwise it only worsen code readability and maintainability.