dynamic sql and how to escape special characters? - sql

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.

Related

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

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 + %'')'

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

Change dynamic SQL from using Exec to sp_executesql

I am a beginner in SQL and esspecially dynamic sql execution.
I am trying to convert some dynamic sql which is inside a store procedure from using EXEC to using sp_executesql.
The issue I have is I don't know what are the exact rules for representing multiple lines of dynamic sql. The existing code is in the form of:
Set #cmd = 'WITH vdate AS' + char(13)
+' (SELECT valueID,' + char(13)
+'username)' +char(13)
+'FROM dbo.table1' + char(13)
+'WHERE username = ''' + convert(varchar(11), #username, 106) + ''' AND' + char(13)
Exec (#cmd)
The above is just a snippet of what it looks like. There are alot more lines and more complicated stuff going on.
I now want to be able to use the sp_executesql way of executing this code because I want to change the username attribute to be a table which accepts multiple usernames.
Please can you advise me what the syntax is for this and how do i make the line before last work?
I have seen that code looks very similar so what i did I changed the +' to be N' at the start of each line and the code compiled but it didn't execute when I tried to use the query in my application.
Thanks, Jetnor.
The only difference is that instead of EXEC (#cmd) you would use EXEC sp_executesql #cmd. Technically speaking, sp_executesql takes NVARCHAR arguments. In order to create an NVARCHAR string you should prefix the string with N like so:
DECLARE #cmd NVARCHAR(1000);
SET #cmd = N'My string'
This denotes that the string is a Unicode string which may contain national character sets. Of course you can omit the N but if you have anything other than ASCII characters in it then it won't work properly and you'll end up with strings that contain a lot of question marks instead of your special characters (CJK - Chinese, Japanese, Korean - character sets for instance) so it is good practice to always prefix NVARCHAR strings with the N.
Some of your confusion arises because you are replacing the string concatenation operator, +, with the national character set identifier, N. You will need both. It is always worth including proper spacing in your code, especially in dynamic SQL because it's easier to read. My preference is to add the spaces at the end of lines where possible, but that's just my personal preference. Also, drop the CHAR(13) stuff unless you really want to print it out in a certain way. Your example would become:
DECLARE #cmd NVARCHAR(1000) --Note this is NVARCHAR, not VARCHAR.
SET #cmd = N'WITH vdate AS '
+ N'(SELECT valueID, '
+ N'username) '
+ N'FROM dbo.table1 '
+ N'WHERE username = ''' + convert(NVARCHAR(11), #username, 106) + ''' AND ' --Note that you are missing a chunk here - AND what?
EXEC sp_executesql #cmd
Note that your CONVERT is incorrect: Firstly you should convert to NVARCHAR (if #username isn't already), and secondly the 106 part does nothing - that's for date formatting. Also you are missing the end of the statement as there is an AND and then nothing. It's always good to use PRINT #cmd to see if the SQL you've generated is valid (it will output the contents of #cmd and you can then copy that and paste it into a workshet in SSMS).
Now, you might want to have output parameters in your dynamic SQL as well which is no problem.
DECLARE #ID INT;
DECLARE #outputValue NVARCHAR(50);
DECLARE #cmd NVARCHAR(1000);
SET #cmd = N'SELECT #value = Value FROM MyTable WHERE ID = ' +
CAST(#ID AS NVARCHAR(5));
EXEC sp_executesql #cmd, '#value NVARCHAR(50) OUT', #outputValue OUT;
SELECT #outputValue;
Traditionally, the main reason for wanting to use sp_executesql as opposed to just EXEC is that you have a better chance of the execution plan being cached and therefore re-used later. I confess I'm not sure if that's still true or not, but sp_executesql is generally the preferred method of executing dynamic SQL. A lot of poeple have an instinctive hatred of dynamic SQL but frankly, like all things, it has it's uses so long as it isn't abused and used where it just isn't required.

While using SP_ExecuteSQL I'm generating a boolean error

I'm very new to SQL, so let me first apologize if my questions come off as trivial or it seems as though I haven't done my work. I am trying to learn how to grasp a lot of these concepts.
Anyway, I'm writing a complex query that will eventually take many parameters. For these parameters I'll be using a comma delimited string to allow multiple variables. (I've solved this issue previously, but not when attempting to execute an SP_ExecuteSQL.
With that being said, here is the bare bones of the query.
DECLARE #system_status varchar(30)
SELECT #system_status = '12,14'
DECLARE #sql nvarchar(4000)
SELECT #sql = 'SELECT [system_status]
FROM VW_Document_Main
WHERE 1=1 '
IF #System_Status = '-1'
Begin
SELECT #sql = #sql + 'and system_status <> 20'
End
ELSE IF #system_status IS NOT NULL AND #system_status NOT IN ('-1','0')
Begin
SELECT #sql = #sql + 'and ' + #system_Status + ' LIKE ''%,'' + system_Status + '',%'''
I'm able to populate a useable query when not building it into an sp_executesql statement, however, since I'll be building about this query it's necessary to take these steps... any thoughts as to why I'm generating the non-Boolean error?
EDIT: Not sure if it's a step in the right direction, but now after reworking the final SELECT statement to read:
SELECT #sql = #sql + 'and '',''' + #system_Status + '',''' LIKE ''%,' + 'system_Status' + ',%'''
It's giving me back a different error: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
It should be worth noting that the error reads: An expression of non-boolean type specified in a context where a condition is expected, near ','.
You're plugging in your literal #system_status value '12, 14' into the final SQL so it looks like this
SELECT [system_status] FROM VW_Document_Main WHERE 1=1 and 12,14 LIKE '%,' + system_Status + ',%'
Which will fail because you don't have 12,14 in quotes. So you'll have to modify the line
SELECT #system_status = '12,14'
to be
SELECT #system_status = '''12,14'''
Alternatively, since you said you're running this through sp_executeSql you should modify your last select to
SELECT #sql = #sql + 'and #system_Status LIKE ''%,'' + system_Status + '',%'''
And change your execute SQL proc to
sp_executesql #sql, N'#system_status varchar(30)', #system_status

nvarchar(max) still being truncated

So I'm writing a stored procedure in MS SQL Server 2008. It's a really long query and I have to write it dynamically, so I create a variable called #Query and make it of type NVARCHAR(MAX). Now, I have been told that in modern versions of SQL Server, NVARCHAR(MAX) can hold a ridiculous amount of data, way more than the original 4000 character maximum. However, #Query is still getting truncated to 4000 characters when I try to print it out.
DECLARE #Query NVARCHAR(max);
SET #Query = 'SELECT...' -- some of the query gets set here
SET #Query = #Query + '...' -- more query gets added on, etc.
-- later on...
PRINT LEN(#Query) -- Prints out 4273, which is correct as far as I can tell
PRINT #Query -- Truncates value to 4000 characters
EXEC sp_executesql #Query -- totally crashes due to malformed (truncated) query
Am I doing something incorrectly, or am I completely wrong about how NVARCHAR(MAX) works?
The problem is with implicit conversion.
If you have Unicode/nChar/nVarChar values you are concatenating, then SQL Server will implicitly convert your string to nVarChar(4000), and it is unfortunately too dumb to realize it will truncate your string or even give you a Warning that data has been truncated for that matter!
When concatenating long strings (or strings that you feel could be long) always pre-concatenate your string building with CAST('' as nVarChar(MAX)) like so:
SET #Query = CAST('' as nVarChar(MAX))--Force implicit conversion to nVarChar(MAX)
+ 'SELECT...'-- some of the query gets set here
+ '...'-- more query gets added on, etc.
What a pain and scary to think this is just how SQL Server works. :(
I know other workarounds on the web say to break up your code into multiple SET/SELECT assignments using multiple variables, but this is unnecessary given the solution above.
For those who hit an 8000 character max, it was probably because you had no Unicode so it was implicitly converted to VarChar(8000).
Warning:
You still Cannot have a Single Unbroken Literal String Larger than 4000 (or 8000 for VarChar).
Literal Strings are those you hard-code and wrap in apostrophe's.
You must Break those Strings up or SQL Server will Truncate each one BEFORE concatenating.
I add ' + ' every 20 lines (or so) to make sure I do not go over.
That's an average of at most 200 characters per line - but remember, spaces still count!
Explanation:
What's happening behind the scenes is that even though the variable you are assigning to uses (MAX), SQL Server will evaluate the right-hand side of the value you are assigning first and default to nVarChar(4000) or VarChar(8000) (depending on what you're concatenating). After it is done Concatenating and figuring out the value (and after truncating it for you) it then converts it into (MAX) when assigning it to your variable, but by then it is too late.
Problem seems to be associated with the SET statement. I think the expression can't be more than 4,000 bytes in size. There is no need to make any changes to any settings if all you are trying to do is to assign a dynamically generated statement that is more than 4,000 characters. What you need to do is to split your assignment. If your statement is 6,000 characters long, find a logical break point and then concatenate second half to the same variable. For example:
SET #Query = 'SELECT ....' [Up To 4,000 characters, then rest of statement as below]
SET #Query = #Query + [rest of statement]
Now run your query as normal i.e. EXEC ( #Query )
To see the dynamic SQL generated, change to text mode (shortcut: Ctrl-T), then use SELECT
PRINT LEN(#Query) -- Prints out 4273, which is correct as far as I can tell
--SET NOCOUNT ON
SELECT #Query
As for sp_executesql, try this (in text mode), it should show the three aaaaa...'s the middle one being the longest with 'SELECT ..' added. Watch the Ln... Col.. indicator in the status bar at bottom right showing 4510 at the end of the 2nd output.
declare #n nvarchar(max)
set #n = REPLICATE(convert(nvarchar(max), 'a'), 4500)
SET #N = 'SELECT ''' + #n + ''''
print #n -- up to 4000
select #n -- up to max
exec sp_Executesql #n
Print truncates the varchar(MAX) to 8000, nvarchar(MAX) to 4000 chars.
But;
PRINT CAST(#query AS NTEXT)
will print the whole query.
Results to text only allows a maximum of 8192 characters.
I use this approach
DECLARE #Query NVARCHAR(max);
set #Query = REPLICATE('A',4000)
set #Query = #Query + REPLICATE('B',4000)
set #Query = #Query + REPLICATE('C',4000)
set #Query = #Query + REPLICATE('D',4000)
select LEN(#Query)
SELECT #Query /*Won't contain any "D"s*/
SELECT #Query as [processing-instruction(x)] FOR XML PATH /*Not truncated*/
Your first problem is a limitation of the PRINT statement. I'm not sure why sp_executesql is failing. It should support pretty much any length of input.
Perhaps the reason the query is malformed is something other than truncation.
The problem with creating dynamic SQL using string expression is that SQL does limit the evaluation of string expressions to 4,000 chars. You can assign a longer string to an nvarchar(max) variable, but as soon as you include + in the expression (such as + CASE ... END + ), then the expression result is limited to 4,000 chars.
One way to fix this is to use CONCAT instead of +. For example:
SET #sql = CONCAT(#sql, N'
... dynamic SQL statements ...
', CASE ... END, N'
... dynamic SQL statements ...
')
Where #sql is declared as nvarchar(max).
I have encountered the same problem today and found that beyond that 4000 character limit, I had to split the dynamic query into two strings and concatenate them when executing the query.
DECLARE #Query NVARCHAR(max);
DECLARE #Query2 NVARCHAR(max);
SET #Query = 'SELECT...' -- some of the query gets set here
SET #Query2 = '...' -- more query gets added on, etc.
EXEC (#Query + #Query2)
Use this PRINT BIG function to output everything:
IF OBJECT_ID('tempdb..#printBig') IS NOT NULL
DROP PROCEDURE #printBig
GO
CREATE PROCEDURE #printBig (
#text NVARCHAR(MAX)
)
AS
--DECLARE #text NVARCHAR(MAX) = 'YourTextHere'
DECLARE #lineSep NVARCHAR(2) = CHAR(13) + CHAR(10) -- Windows \r\n
DECLARE #off INT = 1
DECLARE #maxLen INT = 4000
DECLARE #len INT
WHILE #off < LEN(#text)
BEGIN
SELECT #len =
CASE
WHEN LEN(#text) - #off - 1 <= #maxLen THEN LEN(#text)
ELSE #maxLen
- CHARINDEX(REVERSE(#lineSep), REVERSE(SUBSTRING(#text, #off, #maxLen)))
- LEN(#lineSep)
+ 1
END
PRINT SUBSTRING(#text, #off, #len)
--PRINT '#off=' + CAST(#off AS VARCHAR) + ' #len=' + CAST(#len AS VARCHAR)
SET #off += #len + LEN(#lineSep)
END
Source:
https://www.richardswinbank.net/doku.php?id=tsql:print_big
I was creating a JSON-LD to create a site review script.
**DECLARE #json VARCHAR(MAX);** The actual JSON is about 94K.
I got this to work by using the CAST('' AS VARCHAR(MAX)) + #json, as explained by other contributors:-
so **SET #json = CAST('' AS VARCHAR(MAX)) + (SELECT .....**
2/ I also had to change the Query Options:-
Query Options -> 'results' -> 'grid' -> 'Maximum Characters received' -> 'non-XML Data' SET to 2000000.
(I left the 'results' -> 'text' -> 'Maximum number of characters displayed in each column' as the default)