SQL Server : pattern to include parenthesis and exclude all other characters - sql-server

I am performing pattern matching on #ProductDescription_Glossary in SQL Server 2005 to replace complete words.
#GlossaryKeyword variable contains word to be matched and replaced.
The following code replaces #GlossaryKeyword found at the beginning, at the end and in the end of #ProductDescription_Glossary, but this code cannot handle replacement successfully if #ProductDescription_Glossary contains parentheses at start or end of word
Case 1: this case is working properly - Heather is not replaced with tooltip link with word Heatherd
#GlossaryKeyword = Heather
#ProductDescription_Glossary = Heathered
Case 2: this case fails - In this case Heather is replaced, my requirement is that, heather do not get replaced, as in case 1 so provide me with required pattern.
#GlossaryKeyword = Heather
#ProductDescription_Glossary = (Heathered
Thanks in advance.
Note: #GlossaryKeyword has alpha numeric, hyphen and / character only i,e (0-9, A-Z, a-z, -, /)
#ProductDescription_Glossary contains HTML tags, which are handled by default (may be due to collation settings on my server)
Code:
if PATINDEX ('%[^a-z]' + #GlossaryKeyword + '[^a-z]%','.' + #ProductDescription_Glossary + '.') > 0
BEGIN
SET #ProductDescription_Glossary = REPLACE(#ProductDescription_Glossary,#GlossaryKeyword, '<a target="_blank" id="q_' + CAST (#GlossaryID AS VARCHAR(10)) + '" class="anchor_regular_Mehroon" href="javascript: void(0);">' + #GlossaryKeyword + '</a>')
SET #GlossaryToolTip = #GlossaryToolTip + '<div id="a_q_' + CAST (#GlossaryID AS VARCHAR(10)) + '" class="toolTip_glossary" style="display:none;">' + #GlossaryKeywordDescription + '</div>'
END

Just as ( ) to the not
select PATINDEX('%[^a-z(]'+ 'Heather' + '[^a-z)]%', '(Heathered red, purple, royal and navy)')
And it is not regex.
And you want to exclude ( - not include.

Related

Query fails on "converting character string to smalldatetime data type"

I've been tasked with fixing some SQL code that doesn't work. The query reads from a view against a predicate. The query right now looks like so.
SELECT TOP (100) Beginn
FROM V_LLAMA_Seminare
//Removal of the following line makes the query successful, keeping it breaks it
where Beginn > (select cast (getdate() as smalldatetime))
order by Beginn desc
When I run the above query, I am greeted with the following error.
Msg 295, Level 16, State 3, Line 1
Conversion failed when converting character string to smalldatetime data type.
I decided to remove the WHERE clause, and now it runs returning 100 rows.
At first, I thought that behind the scenes, SQL Server was somehow including my predicate when bringing back the View . But then I investigated how the View was being created, especially the Beginn field, and at no point does it return a String.
Long story short, the column that becomes the Beginn field is a BIGINT timestamp like 201604201369.... The original user transforms this BIGINT to a smalldatetime using the following magic.
....
CASE WHEN ma.datum_dt = 0
THEN null
ELSE CONVERT(smalldatetime, SUBSTRING(CAST(ma.datum_dt AS varchar(max)),0,5) + '-' +
SUBSTRING(CAST(ma.datum_dt AS varchar(max)),5,2) + '-' +
SUBSTRING(CAST(ma.datum_dt AS varchar(max)),7,2) + ' ' +
SUBSTRING(CAST(ma.datum_dt AS varchar(max)),9,2) +':'+
SUBSTRING(CAST(ma.datum_dt AS varchar(max)),11,2) +':' +
RIGHT(CAST(ma.datum_dt AS varchar(max)),2)) END AS Beginn
...
My last attempt at finding the problem was to query the view and run the function ISDATE over the Beginn column and see if it returned a 0 which it never did.
So my question is two fold, "Why does a predicate break something" and two "Where on earth is this string error coming from when the Beginn value is being formed from a BIGINT".
Any help is greatly appreciated.
This problem is culture related...
Try this and then change the first SET LANGUAGE to GERMAN
SET LANGUAGE ENGLISH;
DECLARE #bi BIGINT=20160428001600;
SELECT CASE WHEN #bi = 0
THEN null
ELSE CONVERT(datetime, SUBSTRING(CAST(#bi AS varchar(max)),0,5) + '-' +
SUBSTRING(CAST(#bi AS varchar(max)),5,2) + '-' +
SUBSTRING(CAST(#bi AS varchar(max)),7,2) + ' ' +
SUBSTRING(CAST(#bi AS varchar(max)),9,2) +':'+
SUBSTRING(CAST(#bi AS varchar(max)),11,2) +':' +
RIGHT(CAST(#bi AS varchar(max)),2)) END AS Beginn
It is a very bad habit to think, that date values look the same everywhere (Oh no, my small application will never go international ...)
Try to stick to culture independent formats like ODBC or ISO
EDIT
A very easy solution for you actually was to replace the blank with a "T"
SUBSTRING(CAST(ma.datum_dt AS varchar(max)),7,2) + 'T' +
Then it's ISO 8601 and will convert...
The solution was found after looking through #Shnugo's comment. When I took my query which contained the Bigint->Datetime conversion logic, and put it into a CTE with "TOP 100000000" to avoid any implicit conversion actions, my query worked. Here is what my view looks like now with some unimportant parts omitted.
---Important part---
CREATE VIEW [dbo].[V_SomeView] AS
WITH CTE AS (
SELECT TOP 1000000000 ma.id AS MA_ID,
---Important part---
vko.extkey AS ID_VKO,
vko.text AS Verkaufsorganisation,
fi.f7000 AS MDM_Nr,
vf.f7105 AS SAPKdnr,
CASE WHEN ma.datum_dt = 0 --Conversion logic
CASE WHEN ma.endedatum_dt = 0 --Conversion logic
CONVERT(NVARCHAR(MAX),art.text) AS Art,
.....
FROM [ucrm].[dbo].[CRM_MA] ma,
[ucrm].[dbo].[CRM_fi] fi,
[ucrm].[dbo].[CRM_vf] vf,
[ucrm].[dbo].[CRM_ka] vko,
[ucrm].[dbo].[CRM_ka] art,
[ucrm].[dbo].[CRM_ka] kat
where ma.loskz = 0
and fi.loskz = 0
and vf.loskz = 0
and fi.F7029 = 0
and vf.F7023 = 0
...
GROUP BY ma.id,
vko.extkey,
vko.text,
fi.f7000 ,
vf.f7105,
ma.datum_dt,
ma.endedatum_dt,
....
)
select * FROM CTE;

MS SQL REPLACE based on 1 character to the left of the $

I am not a SQL expert so please forgive me if this is SQL 101 :).
In a select statement there are 2 replace functions. They look for a Servername and it's admin share d$ by it's UNC path. Example '\SERVERNAME\d$'
It then replaces '\SERVERNAME\d$' with 'D:'.
Here is the query currently:
select Replace(p.Path,'\\SERVERNAME\d$','D:') as searchpath
,p.path as fullpath
,s.ShareName
,s.SharePath
,p.Member
,p.Access
From Paths As p
Left Outer Join Shares as s on
Replace(p.Path,'\\SERVERNAME\d$','D:') Like s.SharePath + '\%'
Up until now it has always been d$.
Today my needs have changed and I need the query to find ANY servername UNC path admin share regardless of share letter (c$, d$, e$, f$...etc) and replace it with it's respective drive letter (D:, E:, F:... etc).
My thought is replace function could find the $ and look one character to the left of it to get the proper share letter, then use that for the replace. The issue I have, not being a SQL professional, is that I know SQL can likley do what I need it to do...I just don't know how to get there. I've googled and found some examples, but haven't had any luck in getting them to work.
Any help would be greatly appreciated.
You can use a combination of STUFF, PATINDEX, LEN to get what you want.
Sample Query
DECLARE #ReplaceChar VARCHAR(100) = '[prefixcharacters]\\SERVERNAME\d$[postcharacter]'
DECLARE #SearchString VARCHAR(100) = '\\SERVERNAME\_$'
SELECT
STUFF(#ReplaceChar,PATINDEX('%' + #SearchString + '%',#ReplaceChar),LEN(#SearchString),
UPPER(SUBSTRING(#ReplaceChar,PATINDEX('%' + #SearchString + '%',#ReplaceChar) + LEN(#SearchString) - 2,1)) + ':') as searchpath
WHERE PATINDEX('%' + #SearchString + '%',#ReplaceChar) > 0
Output
[prefixcharacters]D:[postcharacter]
Alternate Query
You can shorten the query if you want to get the previous character before $ as per your title. Something like this
DECLARE #ReplaceChar VARCHAR(100) = '[prefixcharacters]\\SERVERNAME\d$[postcharacter]'
DECLARE #SearchString VARCHAR(100) = '\\SERVERNAME\_$'
SELECT
STUFF(#ReplaceChar,
PATINDEX('%'+#SearchString+'%',#ReplaceChar),
LEN(#SearchString),
UPPER(SUBSTRING(#ReplaceChar,CHARINDEX('$',#ReplaceChar) -1,1)) + ':')
WHERE PATINDEX('%'+#SearchString+'%',#ReplaceChar) > 0
In this query
STUFF replaces your pattern with with the character before $ + ':'
Start of pattern is identified by PATINDEX('%'+#SearchString+'%',#ReplaceChar)
D is identified by getting the charindex of '$' and then getting the previous character using SUBSTRING
What about ΒΈ
select Replace(SUBSTRING(p.path, 14, Len(#spath)-14),'$',':') as searchpath
,p.path as fullpath
,s.ShareName
,s.SharePath
,p.Member
,p.Access
From Paths As p
Left Outer Join Shares as s on
Replace(SUBSTRING(p.path, 14, Len(#spath)-14),'$',':') Like s.SharePath + '\%
select as searchpath
DECLARE #str nvarchar (100)
SET #str = '\\SERVERNAME\d$'
IF #str LIKE '\\SERVERNAME\_$'
SET #str = UPPER(SUBSTRING(#str, 14, 1)) + ':'
SELECT #str
Starting from previous, something like
select UPPER(SUBSTRING(p.path, 14, 1)) + ':' as searchpath
,p.path as fullpath
,s.ShareName
,s.SharePath
,p.Member
,p.Access
From Paths As p
Left Outer Join Shares as s on
SUBSTRING(p.path, 14, 1) + ':' Like s.SharePath + '\%'
I am no mysql expert either :)
Based on the logic you mentioned in the last part of the question, I have used concat and substring to get to the drive letter in the column.
Hope this helps
select replace(path, concat(substring(path, 1, locate('$', path) - 2), substring(path, locate('$', path) - 1, 1) , '$'), concat(substring(path, locate('$', path) - 1, 1) , ':')) as searchpath ...
The remaining part of the query would be the same.

Get a Substring in SQL between two characters and remove all white spaces

I've a strings such as:
Games/Maps/MapsLevel1/Level 1.swf
Games/AnimalWorld/Animal1.1/Level 1.1.swf
Games/patterns and spatial understanding/Level 13.5/Level 13.5.swf
I want to get only file name without its extension(String After last Slash and before Last dot), i.e Level 1 and Level 1.1 and Level 13.5, Even I want to remove all the white spaces and the final string should be in lower case i.e the final output should be
level1
level1.1
level13.5 and so on..
I tried following query but i got Level 1.swf, How do i change this Query?
SELECT SUBSTRING(vchServerPath, LEN(vchServerPath) - CHARINDEX('/', REVERSE(vchServerPath)) + 2, LEN(vchServerPath)) FROM Games
SELECT (left((Path), LEN(Path) - charindex('.', reverse(Path))))
FROM
(
SELECT SUBSTRING(vchServerPath,
LEN(vchServerPath) - CHARINDEX('/', REVERSE(vchServerPath)) + 2,
LEN(vchServerPath)) Path
FROM Games
) A
This would work, I kept your inner substring which got you part way and I added the stripping of the dot.
I have included a sql fiddle link for you to see it in action sql fiddle
Edited:
Following will remove the white space and returns lower case...
SELECT REPLACE(LOWER((left((Path), LEN(Path) - charindex('.', reverse(Path))))), ' ', '')
FROM
(
SELECT SUBSTRING(vchServerPath,
LEN(vchServerPath) - CHARINDEX('/', REVERSE(vchServerPath)) + 2,
LEN(vchServerPath)) Path
FROM Games
) A
Try this:
select
case
when vchServerPath is not null
then reverse(replace(substring(reverse(vchServerPath),charindex('.',reverse(vchServerPath))+1, charindex('/',reverse(vchServerPath))-(charindex('.',reverse(vchServerPath))+1)),' ',''))
else ''
end
This should work fine; with extension removed.
select
REVERSE(
SUBSTRING(
reverse('Games/patterns and spatial understanding/Level 13.5/Level 13.5.swf'),
5,
(charindex('/',
reverse('Games/patterns and spatial understanding/Level 13.5/Level 13.5.swf')) - 5)
))

Easiest way to replace illegal character in XML attributes

I have a trouble with a hand-writting XML. The XML is generated by concat string as this :
ReturnXML += "<agentchimique ";
ReturnXML += "id_prod=\"" + Produit.Id + "\" ";
ReturnXML += "nom_com_prod=\"" + Produit.NomComProd + "\" ";
ReturnXML += "nom_four=\"" + Produit.Fournisseur.NomFour + "\" ";
ReturnXML += "PhraseR=\"" + Produit.PhrasesR.Replace( "<br/>", "#" ) + "\" ";
ReturnXML += "PhraseS=\"" + Produit.PhrasesS.Replace( "<br/>", "#" ) + "\" ";
ReturnXML += "numfds_prod=\"" + Produit.NumfdsProd.ToString() + "\" ";
ReturnXML += "transverse_prod=\"" + Produit.TransverseProd.ToString() + "\" ";
This is only a few part of the whole XML and as you can see, all data is in attribute...
But some parameters as Produit.PhrasesR or Produit.PhrasesS can contains illegal character as < > or &.
This XML is stored as text in SQL and executed by SQL server, with the stored procedure sp_xml_preparedocument to be read in a report services.
This XML can be used too as data to generate as a webpage.
So, what is the quickest and easiest solution to resolve this encoding problem, as know I can not rewrite a true compliant XML (I don't have any time for this), and this XML is used on many place in my project (replace character would be the easiest but it demand to replace back in reports and webpage).
Thanks for your helping.
You can use SecurityElement.Escape
ReturnXML += "nom_four=\"" + SecurityElement.Escape(Produit.Fournisseur.NomFour) + "\" ";
This will replace < with <, > with > etc.
Although you really should use XDocument to build your xml correctly.
You have to escape them with their ASCII equivalents:
'<' must become '<'
'>' must become '>'
'&' must become '&'
No shortcuts that I know of.
I don't think your issue is SQL. Magic characters in XML make it ill-formed. Is the XML you create valid?

Returning non-printable sybol (enter) to SQL Server from C# via CLR

I have the following CLR function:
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString PrintText(SqlString text)
{
// Put your code here
return new SqlString(text.Value);
}
And I want to get an enter symbol when passing '\r\n'
to it. But insteat I get '\r\n'
Could you please tell me what's wrong with this code.
Thank you.
In T-SQL you don't write a line break as \r\n. Instead you just use a line break:
'this
is a
string
in SQL
with
line breaks'
If you pass a string with \r\n to the C# code, nothing magical happens, it doesn't automatically get converted. The backslash character is just a character like any other. It's when you use the backslash in a literal string in the code that the compiler uses it as an escape code, and puts the control characters in the actual string.
You could always:
return new SqlString(text.Value.Replace("\\r\\n", "\r\n"));
From the SQL side, you could insert char(13) + char(10) into your T-SQL literals like so:
DECLARE #text varchar(100)
SET #test = 'this' + char(13) + char(10)
+ 'is a' + char(13) + char(10)
+ 'string' + char(13) + char(10)
+ 'in SQL' + char(13) + char(10)
+ 'with' + char(13) + char(10)
+ 'line breaks'
Though this works, it is much more verbose than Guffa's answer.
However, this technique can also be used to insert any character of the default code page into a string. The Function CHAR(int) accepts any integer between 0 and 255. Values outside that range cause it to return null. The function NCHAR(int) accepts values up to 65535 and inserts the corresponding unicode characters into a unicode string. Functions ASCII(char) and UNICODE(nchar) perform the inverse operations.

Resources