LIKE pattern T-SQL - sql-server

I have wrote a phraser which is phrasing a quite long string into small pieces, after the phrasing of one item is completed, it removes him from #input and continue, until it wont be able to find any items to phrase.
I am selecting items based on LIKE pattern.
In some cases, there are however it is selecting some other parts of the message, and then it's end in infinitive loop.
The pattern I am looking for to be selected using LIKE clause is in format of :
(Any number from 1 to 9) + (variable length A-Z only) + '/' +
(variable length A-Z only)+space of Cr or Lf or CrLf.
--This is what I do have:
DECLARE #match NVarChar(100)
SET #match = '%[1-9][a-z]%'
DECLARE #input1 varchar(max),#input2 varchar(max)
SET #input1 ='1ABCD/EFGH *W/17001588 *RHELLO SMVML1C'
DECLARE #position Int
SET #position = PATINDEX(#match, #input1);
SELECT #position;
--after the loop- it is also 'catching' the 1C at the end of the string:
SET #input2 = '*W/17001588 *RHELLO SMVML1C'
SET #position = PATINDEX(#match, #input2);
SELECT #position
---In order to eliminate this, I have tried to change #match:
SET #match = '%[1-9][a-z][/][a-z]%'
SET #position = PATINDEX(#match, #input1);
SELECT #position --postion is 0, so the first item, that should have been selected, wasn't selected
SET #position = PATINDEX(#match, #input2);
SELECT #position --postion is 0
Many thanks for help!

Try changing your match variable/criteria to this:
SET #match = '%[1-9][a-z]%[/][a-z]%'
This will get you the desired result. Loosely translated it is saying "Get me the starting position of the first match where the pattern is [anything]-[number from 1-9]-[single letter from a-z]-[anything]-[slash]-[single letter from a-z]-[anything].
Hope this helps!

I agree with the comments above; this is a regex problem that needs to be solved with regex tools. I would recommend the assembly created by SimpleTalk. You can get their code and read their very thorough article.
Unfortunately this solution requires serious admin rights to the database and server, so it won't be portable as a script. But I think these functions are worth any effort it takes to get the installed if you develop on the same database on a regular basis.
Just be forwarned, regex is a real processing hog and undermines much of the indexing efficiency in SQL Server. Use it only when you can't use like.

I don't know if this solves your entire problem , but if you can prefix your input with a space, you can modify the pattern to avoid matching a number without a preceding space.
set #input = ' '+#input;
set #match = '% [1-9][a-z]%';
If you need your pattern to account for other whitespace like Cr and Lf, your pattern could look like this:
set #match = '%[ '+char(13)+char(10)+'][1-9][a-z]%';

Related

smart replace (re-phrase) text in SQL

I have a string in SQL with the following structure:
#number <logical> #number <logical> ....
ex:
#SQL='((#1 or #2) and (#10 or #21))'
I would like to update the string to be:
#SQL='((con_1=1 or con_2=1) and (con_10=1 or con_21=1))'
meaning remove the '#' and replace it with 'con_', leave the number (1 digit or more) as is and add '=1' after the digit.
Any idea how to do it?
Please avoid composing function or procedure for that.
you may use patindex, stuff or any other built in function for that.
First split the string into records on the '#' char that starts the value needing processed.
STUFF can be used to insert the '=1' after the last digit of the value in each record. I think searching for the last number is harder than searching for the first. The reverse allows searching for the first instead of the last. Because the string is reversed, insert the reverse '1=' at the start of the reverse number. If there is no number, return the revered value as is.
Concatenate it all back with 'con_' instead of '#'.
declare #SQL varchar(200) = ' ((#1 or #2) and (#10 or #21)) #33 #55 ';
with split as (
select value, REVERSE(value) as [rev], PATINDEX('%[0-9]%', REVERSE(value)) as [pat]
FROM STRING_SPLIT(#SQL, '#')
), fixed as (
select value, rev, pat, CASE WHEN pat= 0 THEN REVERSE(rev) ELSE REVERSE(STUFF(rev, pat, 0, '1=')) END as [fix]
FROM split
) select STRING_AGG(fix, 'con_') from fixed
Result: ((con_1=1 or con_2=1) and (con_10=1 or con_21=1)) con_33=1 con_55=1
It would be nice if Regex support was included in SQL server. I imagine to do so would require a careful implementation to avoid using all the server resources. A regex search on a millions of records or varchar(max) columns....
Found this solution which uses SQL# (SQLsharp).
SQL# is a .NET / CLR library that resides in a SQL Server 2005 (or newer) database and provides a suite of User-Defined Functions, Stored Procedures, User-Defined Aggregates, and User-Defined Types.
My solution:
declare #SQL varchar(200) = '#1 OR(#2 AND #3 AND (#4 or #5 or #6) AND (#7 or #8))'
SELECT SQL#.RegEx_Replace4k(#SQL, N'(#)+(\d*)', N'CON_$2=1', -1, 1, N'')

SQL XML modify replace

I need help in replacing the XML tag value. Sample code is as follows:
declare #l_runtime_xml XML
declare #l_n_DrillRepID numeric(10)
declare #griddrillparam nvarchar(30)
declare #l_s_DrillBtColumnTag nvarchar(256)
declare #l_s_BTNameSecond nvarchar(30)
set #l_n_DrillRepID =1538
set #griddrillparam = 'userID'
set #l_s_DrillBtColumnTag = 'V_userID'
set #l_s_BTNameSecond = 'l_s_userID'
declare #l_runtime_xmlAAA nvarchar(max)
set #l_runtime_xmlAAA = N'<REPORT_RUNTIME_XML><USER_ID>AISHU</USER_ID><SHEET><SHEET_NO>1</SHEET_NO><DRILLTHRU_PARAM><ENT_RPT><ENT_RPT_ID>1537</ENT_RPT_ID><ENT_RPT_NAME>Reddy111</ENT_RPT_NAME><DEFAULT>1</DEFAULT><CRITERIA><DISPLAY/><HIDDEN/></CRITERIA><COLUMN_HEADER>N</COLUMN_HEADER><DISPLAY_BUTTON>N</DISPLAY_BUTTON><PARAMLIST><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>userID</NAME><BT_NAME/><V_userID>(none)</V_userID></PARAM><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>langID</NAME><BT_NAME/><V_langID>(none)</V_langID></PARAM><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>l_s_userID</NAME><BT_NAME/><V_l_s_userID>(none)</V_l_s_userID></PARAM><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>a_i_langID</NAME><BT_NAME/><V_a_i_langID>(none)</V_a_i_langID></PARAM></PARAMLIST></ENT_RPT><ENT_RPT><ENT_RPT_ID>1538</ENT_RPT_ID><ENT_RPT_NAME>Reddy333</ENT_RPT_NAME><DEFAULT>0</DEFAULT><CRITERIA><DISPLAY/><HIDDEN/></CRITERIA><COLUMN_HEADER>N</COLUMN_HEADER><DISPLAY_BUTTON>N</DISPLAY_BUTTON><PARAMLIST><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>userID</NAME><BT_NAME/><V_userID>(none)</V_userID></PARAM><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>langID</NAME><BT_NAME/><V_langID>(none)</V_langID></PARAM><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>l_s_userID</NAME><BT_NAME/><V_l_s_userID>(none)</V_l_s_userID></PARAM><PARAM><COLTYPE/><POSITION>-999</POSITION><BT_COLUMN_NAME/><NAME>a_i_langID</NAME><BT_NAME/><V_a_i_langID>(none)</V_a_i_langID></PARAM></PARAMLIST></ENT_RPT></DRILLTHRU_PARAM></SHEET></REPORT_RUNTIME_XML>'
select #l_runtime_xml = cast(#l_runtime_xmlAAA as XML)
SET #l_runtime_xml.modify('replace value of ((//SHEET/DRILLTHRU_PARAM/ENT_RPT[(ENT_RPT_ID/text())[1] eq sql:variable("#l_n_DrillRepID")]/PARAMLIST/PARAM[(NAME/text())[1] eq sql:variable("#griddrillparam")]/#l_s_DrillBtColumnTag))[1] with sql:variable("#l_s_BTNameSecond")')
set #l_runtime_xmlAAA = cast(#l_runtime_xml as nvarchar(max))
select #l_runtime_xmlAAA
I think your issue is here:
.../#l_s_DrillBtColumnTag))[1]
If I understand this correctly, you are trying to access an element of the name "V_userId" by putting a variable in that place. But this will not work...
Your question is not quite clear to me (and admittably your structure isn't either, it seems too complicated...). And what do you mean with delete the last child of a node?
Your query would be the following:
Find the "ENT_RPT" with the given number, there find the "PARAM" whose name is what's given and on the same level find the element with the name given, which is to be replaced (see local-name()). Replace this with the value given:
SET #l_runtime_xml.modify('replace value of (//SHEET/DRILLTHRU_PARAM/ENT_RPT[ENT_RPT_ID/text()=sql:variable("#l_n_DrillRepID")]/PARAMLIST/PARAM[NAME/text()=sql:variable("#griddrillparam")]/*[local-name(.)=sql:variable("#l_s_DrillBtColumnTag")]/text())[1] with sql:variable("#l_s_BTNameSecond")')
On the first sigth I assume, that you are dealing with named parameters. It was much better to put the value of these parameters in an element with the same name for all of them (e.g. <VALUE>something</VALUE>. They are specified via "NAME" anyway.
And even better was a structure with attributes, something like
...<PARAM Name="userName" position="-999" moreAttributes...>SomeValue</PARAM>
This would make your navigation much easier and less erronous...

How to use CASE in WHERE clause for short circuiting CONTAINS

I have a Stored Procedure for searching that takes several optional parameters. One of those is #keywords defined as
#keywords nvarchar(1000) = null,
If #keywords is null or empty string, I want to short circuit, otherwise I need to search a full text index. My logic used to look like this:
WHERE
(#keywords IS NULL OR CONTAINS( (Title, CrossRef, company_name), #keywords))
AND
-- other search terms
However, I just discovered that OR is not guaranteed to short circuit, so sometimes this returns an error for "empty full-text predicate." Apparently CASE is supposed to be guaranteed to short circuit, but some versions of SQL server have a bug where this isn't the case.
Here's what I'm trying:
WHERE
(1 = CASE
WHEN #keywords IS NULL THEN 1
WHEN #keywords = '""' THEN 1
ELSE (CASE WHEN CONTAINS( (Title, CrossRef, company_name), #keywords) THEN 1 ELSE 0 END)
)
AND
-- other search terms
This still gives the "empty full-text predicate" error. I'd be happy to just replace #keywords with something that will always match, but I'm not sure how to do that either.
Since you're already using a SP, why not just throw this into IF THEN ELSE blocks to implement your short-circuit logic. Not only will it be easier to read, but the optimizer will like it better as well.
Here's what I ended up doing. At the top of the SP:
DECLARE #fix_keywords nvarchar(1000)
SET #fix_keywords = #keywords
IF #keywords IS NULL OR #keywords = '' SET #fix_keywords = 'zqxvMatchNothing321'
and then
WHERE
(#keywords IS NULL OR #keywords = '' OR CONTAINS( (Title, CrossRef, company_name), #fix_keywords))
AND
-- other search terms
I realize this is a bit hacky, but I think it's the best solution in my case, since I don't have to duplicate the entire query in an IF/ELSE.
Another possible solution would be to use dynamic SQL.

SQL Server 2008 select statement that ignores non alphanumeric characters

I have an interesting SQL Server search requirement.
Say I have a table with Part Numbers as follows:
PARTNO DESCRIPTION
------ -----------
ABC-123 First part
D/12a92 Second Part
How can I create a search that will return results if I search, say, for 'D12A'?
I currently have a full text search set up for the description column, but I am looking to find parts that match the part no even when users don't include the / or - etc.
I'd rather do this in a single SQL statement rather than creating functions if possible as we only have read access to the DB.
You could do something like:
SELECT * FROM PART_TABLE
WHERE REPLACE(REPLACE(PARTNO,'/', ''),'-','') LIKE '%D12A%'
This would work for the 2 characters you specified and could be extended for more character like so:
SELECT * FROM PART_TABLE
WHERE REPLACE(REPLACE(REPLACE(PARTNO,'/', ''),'-',''),*,'') LIKE '%D12A%'
Probably not the most elegant of solutions unless your special characters are limited. Otherwise I'd suggest writing a Function to strip out non-alphanumeric characters.
Here is an example of such a function:
CREATE FUNCTION dbo.udf_AlphaNumericChars
(
#String VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #RemovingCharIndex INT
SET #RemovingCharIndex = PATINDEX('%[^0-9A-Za-z]%',#String)
WHILE #RemovingCharIndex > 0
BEGIN
SET #String = STUFF(#String,#RemovingCharIndex,1,'')
#RemovingCharIndex = PATINDEX('%[^0-9A-Za-z]%',#String)
END
RETURN #String
END
------- Query Sample (untested)---------
SELECT *
FROM PART_TABLE
WHERE DBO.udf_AlphaNumericChars(PARTNO) LIKE '%D12A%'
Taken From: http://sqlserver20.blogspot.co.uk/2012/06/find-alphanumeric-characters-only-from.html

Issue in replacing single quote in SQL server

I have been seraching solution for this issue .Though this particular question has been discussed many times in this forum, i did not get any proper answer for my problem.
I will be getting data from 3rd party which can contain single quote.This data need to be inserted into data base and when it contains single quote it fails and throws following error:
Msg 105, Level 15, State 1, Line 7
Unclosed quotation mark after the character string '
---Following is c++ code to pass trandata as input along with other parameters and invoke fn_stripsingleQuote10 function from SQL server:
strSQLText = "declare #returnType as varchar(max)\n EXEC #returnType = CABINET..fn_stripsingleQuote10 ";
sqlTxtParams.Format("'%s', '%s', '%s', tranData, sing_quote, double_sing_quote);
strSQLText += sqlTxtParams;
----My sql function(fn_stripsingleQuote10) to replace single quote
USE [cabinet]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[fn_stripsingleQuote10](
#strip varchar(Max),#patern varchar(5),#replace varchar(5)
)
returns varchar(Max) as begin
declare #CleanString varchar(Max)
SET #CleanString=(REPLACE(#strip,#patern, #replace))
return #CleanString
end
sample output:
ex:
declare #returnType as varchar(max) EXEC #returnType = CABINET..fn_stripsingleQuote10 'fsds'd','''',''''''
I feel the way i am invoking the function is not proper.Please provide a solution .
Strictly speaking you're looking for QUOTENAME, which does exactly what you're asking:
'quote_character' Is a one-character string to use as the delimiter.
Can be a single quotation mark ( ' ), a left or right bracket ( [ ] ),
or a double quotation mark ( " ).
However, it is very very likely that your code is exposed to SQL Injection right now and you should actually use a parameter. It is almost never required to concatenate input into the resulted executed SQL.

Resources