parsing character error in SQL via XML - sql-server

I have the below code which errors when I run it because it has the "&" sign and can not convert it.
the result should display "testing &". however if I change the xml bit to "testing &" it works. I need a way to replace it so that it does not error.
Declare #Request XML = null
If #Request IS NULL
BEGIN
SET #Request = '
<Request>
<ProductRequest>
<ProductName>testing &</ProductName>
</ProductRequest>
</Request>'
END
select #Request.value ('(//ProductName)[1]','nvarchar(100)')

You are probably looking for this:
Declare #Request XML = null
If #Request IS NULL
BEGIN
SET #Request = (SELECT 'testing &' AS ProductName FOR XML PATH('ProductRequest'),ROOT('Request'));
END
select #Request.value ('(//ProductName)[1]','nvarchar(100)')
Some background:
XML is more than just some text with extra characters. XML should never be generated just by typing (as in your case) or by string concatenation (often seen). Use the proper method to generate your XML and all encoding issues are solved for you implicitly.
Look at the XML generated and you will find, that the & is found as &. While reading this with value() the re-encoding is done for you - again implicitly.
You should not start to do own REPLACE approaches. Next day someone enters a <or > or another not supported character and you have the same troubles again.

The & is a reserved/special character in XML. It should be &amp ; and remove space between &amp and ;
as the next:
Declare #Request XML = null
If #Request IS NULL
BEGIN
SET #Request = '
<Request>
<ProductRequest>
<ProductName>testing &</ProductName>
</ProductRequest>
</Request>'
END
select #Request.value ('(//ProductName)[1]','nvarchar(100)')

You need to specify the entity reference & in the XML string for the ampersand:
DECLARE #Request XML = NULL;
IF #Request IS NULL
BEGIN
SET #Request = '
<Request>
<ProductRequest>
<ProductName>testing &</ProductName>
</ProductRequest>
</Request>';
END;
SELECT #Request.value ('(//ProductName)[1]','nvarchar(100)');
See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references

Related

How to check IS NULL in dynamic query in sql server

I did following store procedure using dynamic query, see the following blueprint of code
ALTER PROCEDURE [dbo].[usp_Report] (
#LocationId INT = NULL
,#UserId INT = NULL)
DECLARE #miscquery NVARCHAR (MAX);
begin
SET #miscquery='
SELECT
,A.AgreementNumber AS Contract
,A.AgreementId
FROM tblAgreement A
WHERE
AND (A.IsDeleted = 0 or A.IsDeleted is null)
AND (
(
' + convert(NVARCHAR(30), #LocationId) + ' IS NULL
AND (
A.CheckoutLocation IN (
SELECT LocationId
FROM [dbo].[tblUserLocations]
WHERE UserID = ' + convert(VARCHAR(10), #userId) +'
AND IsDeleted = 0
)
OR A.CheckoutLocation IS NULL
)
)
OR A.CheckoutLocation = ' + convert(VARCHAR(10), #LocationId) +'
)'
EXEC (#miscquery)
end
)
here when i execute the query with #Locationid is null, results doesn't return table, it returns like following
(63 rows affected)
(2325 rows affected)
please help me. thank you
The code you have there cannot be your actual code, firstly because right at the start you try to set a variable called #miscquery before you declare it. There's also no reason for this code to be dynamic, so it's clear you're doing some other stuff as well.
I will take it as a given that for some reason you "need" dynamic SQL. I will put in the standard warning about sanitising your inputs. That was it.
OK. Even if #miscquery had been declared, the code as written will not produce any results. It will either throw a syntax error, or do nothing, depending on your setting for concat_null_yields_null.
Let's take the likely case: you have the default setting for this, which means that when you concatenate a varchar to null, the result is null.
Observe the following code:
declare #locationId int = null;
select 'this sentence ends with...' + convert(nvarchar(30), #locationId);
What will be the output?
"This sentence ends with... null"
"This sentence ends with..."
null
The answer is 3. And notice: that's not a string with the value "null". That's just null. When you convert the null value to a string, you don't get a string with the value "null", you get the null value.
OK, so now we try to add null to the end of our string. When you try to concatenate the null value to a string with the + operator, the entire result is null.
Therefore, your entire #miscquery variable is null at the end of the assignment.
So what you are then doing is the same as this:
declare #myquery varchar(max) = null;
exec sp_executesql #myquery
Which is valid, and doesn't throw any error, but does nothing. Then the rest of your code does whatever it does, and produces the results you see.
if concat_null_yields_null was set to off then you would get a syntax error, because the resulting text would not be valid SQL when you handed it to sp_executesql.
As Dan said, the best and safest solution would be to parameterize your inputs. But putting that aside, the solution you need would look something like the following. In the iif() function, I look at the value of #locationId. If it is null, then I want the string "null". If it is not null, then I want the string conversion of whatever value it has.
declare #locationId int = null;
declare #query varchar(max) =
'select * from mytable where '
+ iif(#locationId is null, 'null', convert(nvarchar(30), #locationId))
+ ' is null';
print #query;

SQL Server 2014 - Parsing XML with Cyrillic Characters

I'm parsing an xml file but have problem with cyrillic characters:
this is the relevant part of the stored Procedure
SOAP input to parse:
'<?xml version="1.0"?>
<soapenv:Envelope xmlns:.......>
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<GetResponse>
<BuyerInfo>
<Name>Polydoros Stoltidys</Name>
<Street>Луговой проезд дом 4 корпус 1 квартира 12</Street>
</BuyerInfo>
</GetResponse>
</soapenv:Body>
</soapenv:Envelope>'
Stored Procedure
CREATE PROCEDURE dbo.spXML_ParseSOAP
(
#XML XML
)
AS
SET NOCOUNT ON;
DECLARE #S nvarchar(max)='',
#C nvarchar(max)='',
#D nvarchar(max)=''
SELECT
#C= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',#C)=0, CONCAT( ISNULL(#C + ',','') , QUOTENAME(T.X.value('local-name(.)', 'nvarchar(100)'))), #C),
#D= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',#CP)=0, CONCAT( ISNULL(#D + ',N','') , '''', T.X.value(N'text()[1]', 'nvarchar(max)'),''''), #D),
FROM #XML.nodes('//*[count(child::*) = 0]') AS T(X)
WHERE T.X.value(N'local-name(.)', 'nvarchar(500)')
IN (select name from Customers.sys.columns where [object_id]=#O and is_identity=0)
SET #S=N'INSERT INTO Sales.dbo.ShippingAddress ('+#C+',ShippingAddressID) VALUES ('+#D+','''+#FADR+''')
Print #S
the problem is that #S looks like this
INSERT INTO Sales.dbo.ShippingAddress ([Name],[Street1],ShippingAddressID)
VALUES
(N'Polydoros Sample',N'??????? ?????? ??? 4 ?????? 1 ???????? 12','KkQ0LhbhwXfzi+Ko1Ai6s+SDZRT2kYhYC3vM2x2TB5Y=')
where Cyrillic Charachters are transformed into ???
I put the N before all input but problem is clearly before:
I can suppose is in the
T.X.value(N'text()[1]', 'nvarchar(max)')
but I do not know how solve it.
Can suggest a solution?
Thanks
Your DECLARE #XML line is wrong. The string literal needs to be prefixed with a capital N. The characters are getting converted to ? in the interpretation of that literal.
Also, you have not prefixed all string literals with a capital-N, but you have at least one of them prefixed (the first one in the SET #S = N' line, and so the rest of the literals (which are VARCHAR without the N prefix) will be implicitly converted to NVARCHAR.
The following adaptation of your updated code shows this behavior, and how placing the N prefix on the input string (prior to calling the Stored Procedure) fixes the problem:
DECLARE #XML XML = N' <!-- remove the N from the left to get all ???? for "Street"-->
<BuyerInfo>
<Name>Polydoros Stoltidys</Name>
<Street>Луговой проезд дом 4 корпус 1 квартира 12</Street>
</BuyerInfo>
';
DECLARE #S nvarchar(max)='',
#C nvarchar(max)='Street',
#D nvarchar(max)=''
SELECT
#D= IIF (T.X.value('local-name(.)', 'nvarchar(100)') = N'Street',
T.X.value('./text()[1]', 'nvarchar(100)'),
#C)
FROM #XML.nodes('//*[count(child::*) = 0]') AS T(X)
SET #S=N'INSERT INTO Sales.dbo.ShippingAddress ('
+ #C+',ShippingAddressID) VALUES (N'''+#D+''',''a'') '
Print #S;
Also, SQL Server XML does not ever store the <?xml ... ?> declaration line, so you might as well remove it from the beginning of the literal value.
First of all: If this solves your problem, please accept srutzky's answer, it is the correct answer to solve your initial example with the declared variable. (but you may vote on this :-) ).
This is just an example to show the problem:
Try this
SELECT 'Луговой проезд'
SELECT N'Луговой проезд'
And now try this:
CREATE PROCEDURE dbo.TestXML(#xml XML)
AS
BEGIN
SELECT #xml;
END
GO
EXEC dbo.TestXML '<root><Street>Луговой проезд дом 4 корпус 1 квартира 12</Street></root>';
returns
<root>
<Street>??????? ?????? ??? 4 ?????? 1 ???????? 12</Street>
</root>
While this call (see the leading "N")
EXEC dbo.TestXML N'<root><Street>Луговой проезд дом 4 корпус 1 квартира 12</Street></root>';
returns
<root>
<Street>Луговой проезд дом 4 корпус 1 квартира 12</Street>
</root>
Conclusio
This does not happen within your procedure. The string you pass over to the stored procedure is wrong before you even enter the SP.

MS SQL - CONTAINS full-text search w/ variable number of values, not using dynamic sql

I've created a full-text indexed column on a table.
I have a stored procedure to which I may pass the value of a variable "search this text". I want to search for "search", "this" and "text" within the full-text column. The number of words to search would be variable.
I could use something like
WHERE column LIKE '%search%' OR column LIST '%this%' OR column LIKE '%text%'
But that would require me to use dynamic SQL, which I'm trying to avoid.
How can I use my full-text search to find each of the words, presumably using CONTAINS, and without converting the whole stored procedure to dynamic SQL?
If you say you definitely have SQL Table Full Text Search Enabled, Then you can use query like below.
select * from table where contains(columnname,'"text1" or "text2" or "text3"' )
See link below for details
Full-Text Indexing Workbench
So I think I came up with a solution. I created the following scalar function:
CREATE FUNCTION [dbo].[fn_Util_CONTAINS_SearchString]
(
#searchString NVARCHAR(MAX),
#delimiter NVARCHAR(1) = ' ',
#ANDOR NVARCHAR(3) = 'AND'
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
IF #searchString IS NULL OR LTRIM(RTRIM(#searchString)) = '' RETURN NULL
-- trim leading/trailing spaces
SET #searchString = LTRIM(RTRIM(#searchString))
-- remove double spaces (prevents empty search terms)
WHILE CHARINDEX(' ', #searchString) > 0
BEGIN
SET #searchString = REPLACE(#searchString,' ',' ')
END
-- reformat
SET #searchString = REPLACE(#searchString,' ','" ' + #ANDOR + ' "') -- replace spaces with " AND " (quote) AND (quote)
SET #searchString = ' "' + #searchString + '" ' -- surround string with quotes
RETURN #searchString
END
I can get my results:
DECLARE #ftName NVARCHAR (1024) = dbo.fn_Util_CONTAINS_SearchString('value1 value2',default,default)
SELECT * FROM Table WHERE CONTAINS(name,#ftName)
I would appreciate any comments/suggestions.
For your consideration.
I understand your Senior wants to avoid dynamic SQL, but it is my firm belief that Dynamic SQL is NOT evil.
In the example below, you can see that with a few parameters (or even defaults), and a 3 lines of code, you can:
1) Dynamically search any source
2) Return desired or all elements
3) Rank the Hit rate
The SQL
Declare #SearchFor varchar(max) ='Daily,Production,default' -- any comma delim string
Declare #SearchFrom varchar(150) ='OD' -- table or even a join statment
Declare #SearchExpr varchar(150) ='[OD-Title]+[OD-Class]' -- Any field or even expression
Declare #ReturnCols varchar(150) ='[OD-Nr],[OD-Title]' -- Any field(s) even with alias
Set #SearchFor = 'Sign(CharIndex('''+Replace(Replace(Replace(#SearchFor,' , ',','),', ',''),',',''','+#SearchExpr+'))+Sign(CharIndex(''')+''','+#SearchExpr+'))'
Declare #SQL varchar(Max) = 'Select * from (Select Distinct'+#ReturnCols+',Hits='+#SearchFor+' From '+#SearchFrom + ') A Where Hits>0 Order by Hits Desc'
Exec(#SQL)
Returns
OD-Nr OD-Title Hits
3 Daily Production Summary 2
6 Default Settings 1
I should add that my search string is comma delimited, but you can change to space.
Another note CharIndex can be substanitally faster that LIKE. Take a peek at
http://cc.davelozinski.com/sql/like-vs-substring-vs-leftright-vs-charindex

Using PATINDEX to remove characters

I am using a function that I found here and else where on the internet to try and strip illegal characters from a field.
Create Function [epacube].[StripSpecs](#myString varchar(500),
#invalidChars varchar(100)) RETURNS varchar(500) AS Begin
While PatIndex(#invalidChars, #myString) > 0
Set #myString = Stuff(#myString, PatIndex(#invalidChars, #myString), 1, '')
Return #myString End
in my table I have set my field value to be: set DATA_NAME = 'Pro$d)uc^t'
If I run this query:
SELECT epacube.StripSpecs (
DATA_NAME
,'%$%') FROM TABLE_DATA
It works and I get a value returned of Prod)uc^t
However, if I add another special character, it no longer works:
SELECT epacube.StripSpecs (
DATA_NAME
,'%$)%') FROM TABLE_DATA
returns my original value Pro$d)uc^t
Does anyone have any suggestion for accomplishing what I need to do?
EDIT
As per the answer below here is the code that worked:
Create Function [epacube].[StripSpecs](#myString varchar(500), #invalidChars varchar(100))
RETURNS varchar(500) AS
Begin
While PatIndex('%[' + #invalidChars + ']%', #myString) > 0
Set #myString = Stuff(#myString, PatIndex('%[' + #invalidChars + ']%', #myString), 1, '')
Return #myString
End
As with LIKE, if you want to specify that one of a set of characters should match, use [] to enclose the set.
SELECT epacube.StripSpecs (
DATA_NAME
,'%[$)]%') FROM TABLE_DATA
Although, personally, given the descriptions of the function and parameters, I'd add the %[ and ]% in StripSpecs, and let the caller just give a list of characters (if you don't want to support any other type of pattern being specified)

Cannot remove trailing space in SQL Server 2005

This is in SQL Server 2005. I have a varchar column and some rows contain trailing space, e.g. abc, def.
I tried removing the trailing space with this command:
update thetable
set thecolumn = rtrim(thecolumn)
But the trailing space remains. I tried to find them using:
select *
from thetable
where thecolumn <> rtrim(thecolumn)
But it returned nothing.
Are there some settings that I am not aware that influences trailing space check?
EDIT:
I know that there is trailing space from SSMS, when I copy paste the value from the grid to the editor, it has trailing space.
Check if the spaces that are not removed have the ASCII code 32.
Try this to replace "hard space" with "soft space":
update thetable set thecolumn = rtrim(replace(thecolumn, char(160), char(32)))
the query was missing equal sign
Are you certain that it is a space (ascii 32) character? You can get odd behavior with other "non-visible" characters. Try running
select ascII(right(theColumn, 1))
from theTable
and see what you get.
Use this Function:
Create Function [dbo].[FullTrim] (#strText varchar(max))
Returns varchar(max) as
Begin
Declare #Ch1 char,#ch2 char
Declare #i int,#LenStr int
Declare #Result varchar(max)
Set #i=1
Set #LenStr=len(#StrText)
Set #Result=''
While #i<=#LenStr
Begin
Set #ch1=SUBSTRING(#StrText,#i,1)
Set #ch2=SUBSTRING(#StrText,#i+1,1)
if ((#ch1=' ' and #ch2=' ') or (len(#Result)=0 and #ch1=' '))
Set #i+=1
Else
Begin
Set #Result+=#Ch1
Set #i+=1
End
End
Return #Result
End
In SQL, CHAR(n) columns are right-padded with spaces to their length.
Also string comparison operators (and most functions too) do not take the trailing spaces into account.
DECLARE #t TABLE (c CHAR(10), vc VARCHAR(10))
INSERT
INTO #t
VALUES ('a ', 'a ')
SELECT LEN(c), LEN(vc), с + vc
FROM #t
--
1 1 "a a"
Please run this query:
SELECT *
FROM thetable
WHERE thecolumn + '|' <> RTRIM(thecolumn) + '|'
and see if it finds something.
It sounds like either:
1) Whatever you are using to view the values is inserting the trailing space (or the appearance thereof- try a fixed-width font like Consolas).
2) The column is CHAR, not VARCHAR. In that case, the column will be padded with spaces up to the length of the column, e.g. inserting 'abc' into char(4) will always result in 'abc '
3) You are somehow not committing the updates, not updating the right column, or other form of user error. The update statement itself looks correct on the face of it.
I had the same issues with RTRIM() AND LTRIM() functions.
In my situation the problem was in LF and CR chars.
Solution
DECLARE #test NVARCHAR(100)
SET #test = 'Declaration status '
SET #test = REPLACE(REPLACE(#test, CHAR(10), ''), CHAR(13), '')

Resources