How to use LIKE in dynamic SQL in a stored procedure? - sql

I wrote a stored procedure using dynamic SQL:
create procedure [dbo].[SearchProduct]
(#ProductId int = null, #ProductName nvarchar(50) = null)
as
declare #SqlStr nvarchar(max)
declare #ParaList nvarchar(2000)
set #SqlStr = 'select p.* from dbo.Product where (1=1) '
if #ProductName is not null
set #SqlStr = #SqlStr + 'and(p.ProductName like '''%' + #ProductName2+'%''')'
set #ParaList='#ProductId2 int , #ProductName2 nvarchar(50)'
EXECUTE SP_EXECUTESQL #SqlStr,#ParaList,#ProductId,#ProductName
But I get an error:
Error in "Like operator" : The data types varchar and varchar are incompatible in the modulo operator.
If I change :
set #SqlStr = #SqlStr + 'and(p.ProductName like ''%' + #ProductName2+'%'')'
I get:
#ProductName2 not declare.

Since you look new to this please accept these notes from me:
As for your question... Your select statement ends with where and you follow it with and
select p.* from dbo.Product where '
Also before % you should have only 2 single quotes not 3.. Like ' '%' +.... + '%' '...
When you do dynamic sql procedures,,, always use print() method first instead of exec to evaluate your sql.
Use case when statement instead of if statements. it will organize your Code much better.
Since dynamic sql is really very bad practice ... Your question should be "how to convert this procedure to normal sql instead of dynamic..."
At the end, please accept my apologies for the lack of samples, mistakes and help links as am answering from my mobile phone.

You have a quote or two too many:
if #ProductName is not null
set #SqlSt r = #SqlStr + 'and (p.ProductName like ''%' + #ProductName2+'%'')';
Within a string, two single quotes represent one single quote in the string. The third single quote then ends the string.

should be below. You have the single quote wrongly
if #ProductName is not null
set #SqlStr=#SqlStr+'and(p.ProductName like ''% + #ProductName2 + %'')'

Related

sql how to write this as a script

I have the following sql query
Select * from Name
where surname in ('test1', 'test2')
which works
But I wanted to do the following
DECLARE #Surname as VARCHAR(100)
set #Surname = 'test1' + ',' + 'test2'
Select * from Name
where surname in #Surname
this is the actual query used
DECLARE #COESNo as VARCHAR(100)
set #COESNo = '121108883' + ',' + '121108890'
declare #sql varchar(max)
set #sql = 'select [LEI_ACCEPT] , [PREFERED_LEI] , [INSPECTION_COMPANY], [INSP_ACCEPT] from [CERTIFICATE_DETAILS] where [Certificate_no] in ('
set #sql = #sql + #COESNo + ')'
exec #sql
get the error
The name 'select [LEI_ACCEPT] , [PREFERED_LEI] , [INSPECTION_COMPANY], [INSP_ACCEPT] from [CERTIFICATE_DETAILS] where [Certificate_no] in (121108883,121108890)' is not a valid identifier.
doesn't seem to work
any ideas
There are two popular solutions.
First one is to build string with query and use sp_executesql to run it.
Second one is to write (or find) function (something like SplitText2Table()) which converts comma separated string to table and write query which use this function -- something like:
select *
from name
where surname in (select item from SplitText2Table(#surnames))
This is not a solution for the exact problem in the question. But maybe (?) you could instead use this workaround:
declare #SurName1 nvarchar(20)
declare #SurName2 nvarchar(20)
set #SurName1='test1'
set #SurName2='test2'
select * from [Name] where [surname] in (#SurName1, #SurName2)
you will need to create your statement and execute - like
declare #sql varchar(max)
set #sql = 'Select * from [Name] where surname in ('
set #sql = #sql + #surname + ')'
After creating the statement just say
exec #sql
You can also check the formed query is correct or not using print
print #sql
Please use print before execution of the command (this will help you in correcting the query is there is any error.).
EDIT:
As per the comment - Since Name is a keyword for SQL - we will need to use square bracket against it. I have modified the statement in my answer.
Edit 2: Based on further comments -
Firstly I would like to know the datatype of Certificate_no column in your database.
If it is a varchar field you will need to have single quotes around each value.
The name SELECT [LEI_ACCEPT] , [PREFERED_LEI] , [INSPECTION_COMPANY], [INSP_ACCEPT] FROM[CERTIFICATE_DETAILS] WHERE [Certificate_no] IN ('121108883','121108890')
You will need to create -
DECLARE #COESNo as VARCHAR(100)
set #COESNo = '''121108883''' + ',' + '''121108890'''
Since Certificate number is varchar the string build will need a single quote in formed query which will appear with three single quotes.
I have created Working DEMO for you on SQL FIDDLE - CLICK HERE

dynamic sql and how to escape special characters?

I have a stored procedure which searches for names based on a string.
if I pass in #SearchTerm as the following value: o'clock
SET #NameSearch = ' (CONTAINS(lmc.Name,''"*' + REPLACE(#SearchTerm,'''','''''') + '*"'')) '
#NameSearch would be set to:
"*o''clock*"
this would return no rows.
however if I just pass in 'clock' then I will get all the results which have a name that contains the word 'clock'.
could someone explain to me how I would be able to escape the ' properly.
You should use parametrized query. Here's an example:
DECLARE #sql nvarchar(max), #paramlist nvarchar(max)
SELECT #sql= 'SELECT Test_Name
FROM [Test]
WHERE (1 = 1)'
SELECT #sql = #sql + ' AND (Test_Name LIKE (#Name + ''Toto''))'
SELECT #paramlist = '#Name nvarchar (256)'
EXEC sp_executesql #sql, #paramlist, #SearchTerm
1. You wrote
if #NameSearch would be set to: "*o''clock*" , but I guess you mean #SearchTerm
2.
What is the result of
SELECT * FROM sys.dm_fts_parser ('"*o''clock*" ', 1033, 0, 0)
One, two or three rows? May be you have problems with wordbreakers. Setup your language first, possibly it is not English (1033).
3.
If I would need to run it dynamically, then I would double apostrophes once more:
DECLARE #sql nvarchar(max)= 'SELECT * FROM sys.dm_fts_parser (''"*o''''clock*" '', 1033, 0, 0)'
exec(#sql)
That is ok, but since you are going to automatically double apostrophes, then you could put extra apostrophes just by error.. Possibly you should dig this direction or present us clear code snippet.

Stored procedure causing syntax error

I have written this stored procedure :
CREATE PROCEDURE [dbo].[spGetCuisines]
#RestaurantID INT ,
#CuisineID NVARCHAR(200)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'SELECT CuisineID, CuisineName
FROM dbo.Cuisine1
WHERE CuisineID IN (
SELECT dbo.Dishes1.CuisineID
FROM dbo.Dishes1
WHERE DishID IN ( SELECT DishID
FROM dbo.RestaurantDish
WHERE RestaurantID = '
+ CAST(#RestaurantID AS NVARCHAR(MAX)) + ' ) )'
IF #CuisineID <> ''
BEGIN
SET #sql += 'AND Cuisine1.CuisineID IN('
+ CAST(#CuisineID AS NVARCHAR(MAX)) +')'
END
EXECUTE sp_executesql #sql;
END
I am using 3 tables with their columns listed below:
Dishes1
DishID
DishName
CuisineID
Price
Cuisine1
CuisineID
CuisineName
Type
DateCreated
DateModified
DateDeleted
RestaurantDish
RestaurantDishID
RestaurantID
DishID
but my stored procedure gives me syntax error on this line:
SET #sql += 'AND Cuisine1.CuisineID IN('+ CAST(#CuisineID AS NVARCHAR(MAX)) +')'
it says:
incorrect syntax near "+"
Can somebody guide me? Does the SQL Server version have something to do with this?
The syntax you are using is only valid on SQL Server 2008 and above. On SQL Server 2005, you'll have to change:
SET #sql += ...
To:
SET #sql = #sql + ...
There's absolutely no need to use dynamic SQL here - so don't ! Also: prefer JOIN over subqueries - joins are typically faster, and quite frankly - code is much easier to read!
Just use:
CREATE PROCEDURE [dbo].[spGetCuisines]
#RestaurantID INT ,
#CuisineID NVARCHAR(200)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX)
SELECT
c.CuisineID, c.CuisineName
FROM
dbo.Cuisine1 c
INNER JOIN
dbo.Dishes1 d ON d.CuisineID = c.CuisineID
INNER JOIN
dbo.Restaurant1 r ON r.DishID = d.DishID
WHERE
r.RestaurantID = #RestaurantID
AND (#CuisineID = '' OR c.CuisineID = #CuisineID)
END
And Aaron Bertrand is absolutely right, of course - this only works if you pass in a single CuisineID as string.
If your #CuisineID parameter contains multiple values then you need something like this instead:
WHERE
r.RestaurantID = #RestaurantID
AND (#CuisineID = '' OR c.CuisineID IN dbo.Split(#CuisineID))
Using a function Split you can split up a comma-separated list of ID's into a table variable and use the IN operator to match to a list of possible values.
are you perhaps passing in a comma delimited string??
If so, there is a better way to handle this, see here:
http://codebetter.com/raymondlewallen/2005/10/26/quick-t-sql-to-parse-a-delimited-string/

What is preferred method for searching table data using stored procedure?

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.

T-SQL creating a dynamic where statement?

I know this is not possible, but is there something that would work? Basically I want the where statement to be dynamic, allowing me to pass it any string, which it will be able to search upon.
Declare #search varchar(80)
set #search = 'RegionID'
Select * from TBL_TripDetails
Where #search = '1'
Thanks for your answers. After reading a few documents, I have decided to use multiple select statements instead of using dynamic sql. thanks!
declare #sql nvarchar(max);
set #sql = N'select * from table where ' + quotename(#search) + N'=''1''';
exec sp_executesql #sql;
See The Curse and Blessings of Dynamic SQL
It is indeed possible, altough is often frowned upon.
Have a look at sp_executesql
Declare #search varchar(80)
set #search = 'RegionID'
declare #query varchar(max)
set #query = "Select * from TBL_TripDetails Where " + #search + " = '1'"
exec #query
DECLARE #search VARCHAR(80)
DECLARE #SQL VARCHAR(8000)
SET #search = 'RegionID'
SET #SQL = 'SELECT * FROM TBL_TripDetails WHERE ' + #search + ' = 1'
EXEC #SQL
Be careful though. Concatenating SQL can allow SQL injection attacks.
I'm a bit confused with your question "pass it any string, which it will be able to search upon". In your example your passing in a field which is being compared against a hard coded value of 1, this doesn't really match your description.
If this is truly what you wanted, then you'll need to use Dynamic SQL. If you just want to be able to support optional search criteria/parameters (e.g. If RegionID has a value set then apply criteria, else ignore criteria), then use the example below.
DECLARE #RegionID AS VARCHAR(1);
SELECT *
FROM TABLE
WHERE (#RegionID Is Null OR #RegionID = '' OR RegionID = #RegionID);
Now, if #RegionID is blank or NULL it won't be used in the criteria.