Locate very next comma to a given substring within string - mysql

I have string {"abc":"dds","def":null,"ghi":fgi"} stored with in a field in database. how can I find very next comma after "def" string?
Actually I have such list of string e.g
{"abc":"dds","def":null,"ghi":"fgi"}
{"abc":"123","def":234,"ghi":"fgd"}
{"abc":"133-d","def":"asd-123","ghi":"fgi"}
.....
I need to remove all occurences of "def" with value i.e I need the result as
{"abc":"dds","ghi":"fgi"}
{"abc":"123","ghi":"fgd"}
{"abc":"133-d","ghi":"fgi"}

I am giving you a working example based on your given data.
SET #str := '{"abc":"133-d","def":"asd-123","ghi":"fgi"}';
SELECT
CONCAT(
SUBSTRING(
#str
FROM
1 FOR LOCATE('def' ,#str) - 2
),
SUBSTRING(
#str
FROM (LOCATE('def' ,#str) + LOCATE(',',SUBSTRING(#str,LOCATE('def' ,#str)))) FOR (LENGTH(#str) + 1 - (LOCATE('def' ,#str) + LOCATE(',',SUBSTRING(#str,LOCATE('def' ,#str)))))
)
) AS newJson;
Demo Here
Explanation:
Explanation based on this string '{"abc":"dds","def":null,"ghi":fgi"}'
SELECT SUBSTRING(#str FROM 1 FOR LOCATE('def' ,#str) - 2 );
Result#1: {"abc":"dds",
SELECT (LOCATE('def' ,#str) + LOCATE(',',SUBSTRING(#str,LOCATE('def' ,#str))));
Result#2: 25 // Location of the comma next to 'def':value
SELECT (LENGTH(#str) + 1 - (LOCATE('def' ,#str) + LOCATE(',',SUBSTRING(#str,LOCATE('def' ,#str)))));
Result#3: 11 // Length of the rest of the string after that very comma
Now get the SUBSTRING(#str FROM position 25 to Result#3 )
It will return
Result #4: "ghi":fgi"}
Now concat result#1 and result#4 to get the full desired string that is {"abc":"dds","ghi":fgi"}
Note: Doing this job in Application level will definitely give you more flexibility.
Edit:
You may create a function named getNewJsonString and use it where you require.
DELIMITER $
DROP FUNCTION IF EXISTS getNewJsonString$
CREATE FUNCTION getNewJsonString(inputStr VARCHAR(300))
RETURNS VARCHAR(255)
READS SQL DATA
BEGIN
DECLARE returnString VARCHAR(255);
SET #str := inputStr;
SET returnString := (SELECT
CONCAT(
SUBSTRING(
#str
FROM
1 FOR LOCATE('def' ,#str) - 2
),
SUBSTRING(
#str
FROM (LOCATE('def' ,#str) + LOCATE(',',SUBSTRING(#str,LOCATE('def' ,#str)))) FOR (LENGTH(#str) + 1 - (LOCATE('def' ,#str) + LOCATE(',',SUBSTRING(#str,LOCATE('def' ,#str)))))
)
)) ;
RETURN returnString;
END$
DELIMITER ;
Sample function Call:
SELECT getNewJsonString('{"abc":"dds","def":null,"ghi":"fgi"}');
Output:
{"abc":"dds","ghi":"fgi"}

Related

How to match any value of search string from a column containing multiple values separated by space in table in sql?

I have a column in table which has multiple values separated by space.
i want to return those rows which has any of the matching values from search string.
Eg:
search string= 'mumbai pune'
This need to return rows matching word 'mumbai' or 'pune' or matching both
Declare #str nvarchar(500)
SET #str='mumbai pune'
create table #tmp
(
ID int identity(1,1),
citycsv nvarchar(500)
)
insert into #tmp(citycsv)Values
('mumbai pune'),
('mumbai'),
('nagpur')
select *from #tmp t
select *from #tmp t
where t.citycsv like '%'+#str+'%'
drop table #tmp
Required Out put:
ID CityCSV
1 mumbai pune
2 mumbai
You can use a splitter function to split your search string out as a table contain the desired search keys. Then you can join your main table with the table containing the search key using the LIKE statement.
For completeness I have included an example of a string splitter function, however there are plenty of example here on SO.
Example string splitter function:
CREATE FUNCTION [dbo].[SplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
The following query demonstrates how the string splitter function can be combined with regular expressions to get the desired result:
SELECT DISTINCT
C.ID
,C.citycsv
FROM #tmp C
INNER JOIN (
SELECT splitdata + '[ ]%' AS MatchFirstWord -- Search pattern to match the first word in the string with the target search word.
,'%[ ]' + splitdata AS MatchLastWord -- Search pattern to match the last word in the string with the target search word.
,'%[ ]' + splitdata + '[ ]%' AS MatchMiddle -- Search pattern to match any words in the middle of the string with the target search word.
,splitdata AS MatchExact -- Search pattern for exact match.
FROM dbo.SplitString(#str, ' ')
) M ON (
(C.citycsv LIKE M.MatchFirstWord) OR
(C.citycsv LIKE M.MatchLastWord) OR
(C.citycsv LIKE M.MatchMiddle) OR
(C.citycsv LIKE M.MatchExact)
)
ORDER BY C.ID
Another approach , by using ReplaceFunction
Its syntax as following:
REPLACE ( string_expression , string_pattern , string_replacement )
so we could reach the target via replacing the every space that separated the values with the next pattern
'%'' OR t.citycsv like ''%'
An example:
Declare #str nvarchar(500),
#Where nvarchar (1000),
#Query nvarchar (4000)
SET #str='mumbai pune'
create table #tmp
(
ID int identity(1,1),
citycsv nvarchar(500)
)
insert into #tmp(citycsv)Values
('mumbai pune'),
('mumbai'),
('nagpur')
select * from #tmp t
Set #Where = 'where t.citycsv like ' + '''%'+ replace (RTRIM(LTRIM(#str)), ' ', '%'' OR t.citycsv like ''%') +'%'''
Set #Query = 'select * from #tmp t ' + #Where
execute sp_executesql #Query
drop table #tmp
The Result:

SQL find text in string

I have a text field in my database:
DECLARE #vchText varchar(max) =
This is a string<>Test1<>Test2<>Test
That #vchText parameter should return like this:
This is a string:
1. Test1
2. Test2
3. Test
Anyone think of a good way to correct this. I was thinking the STUFF and CHARINDEX Functions with a WHILE LOOP...?
Something I should also note would be that there might not be only 1,2,3 items in the list there could be lots more so I can't build it so its static and only handles 1,2,3 it should be able to work for any number of items in the list.
Try this. Break the string into parts.
First part - This is a list:
Second part - 1.Test1 1.Test2 1.Test3
Convert the second part into rows using the delimiter Space. Then add row_number to the rows. Append the row_number and column data.
Finally convert the different rows into single row delimited by space and append it with the first part
DECLARE #NOTE VARCHAR(max) = 'This is a list: 1.Test1 1.Test2 1.Test3',
#temp VARCHAR(max),
#output VARCHAR(max)
SELECT #temp = Substring(#NOTE, Charindex(':', #NOTE) + 2, Len(#note))
SELECT #output = LEFT(#NOTE, Charindex(':', #NOTE) + 1)
SELECT #output += CONVERT(VARCHAR(10), Row_number() OVER (ORDER BY col))
+ Substring(col, Charindex('.', col), Len(col))
+ ' '
FROM (SELECT Split.a.value('.', 'VARCHAR(100)') col
FROM (SELECT Cast ('<M>' + Replace(#temp, ' ', '</M><M>') + '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)) ou
SELECT #output -- This is a list: 1.Test1 2.Test2 3.Test3
I was able to do it with a loop and use the stuff and charindex below.
DECLARE #vchText varchar(max) =
This is a string<>Test1<>Test2<>Test
DECLARE #positionofNextX INT = CHARINDEX('<>', #vchText)
DECLARE #nbrOFListItems INT = 1
WHILE #positionofNextX != 0
BEGIN
SET #NOTE = STUFF( #vchText, #positionofNextX, 4, CAST(#nbrOFListItems AS VARCHAR(1)) + '. ')
SET #positionofNextX = CHARINDEX('<>', #vchText)
--increment the list item number
SET #nbrOFListItems = #nbrOFListItems + 1
END
print #vchText

Space separated string to create dynamic Sql query

I have a space separated string as parameter to my SP. I need to split the strings and compare each one against a column in the database along with wildcard and return the results.
For example:
I have CompanyName 'ABC DataServices Pvt Ltd' I need to split the string by 'space' and compare each word with database field company name with an OR condition.
Something like this:
select *
from CompanyTable
where companyname like '%ABC%'
or companyname like '%DataServices%'
or companyname like '%Pvt%'
or companyname like '%Ltd%'
Can some one help me out to achieve this?
Thanks in advance
Try this SQL User Defined Function to Parse a Delimited String helpful .
For Quick Solution use this ,
SELECT PARSENAME(REPLACE('ABC DataServices Pvt Ltd', ' ', '.'), 2) // return DataServices
PARSENAME takes a string and splits it on the period character. It takes a number as it's second argument, and that number specifies which segment of the string to return (working from back to front).
you can put the index you want in place of 2 in above like
SELECT PARSENAME(REPLACE('ABC DataServices Pvt Ltd', ' ', '.'), 3) --return Pvt
declare a string in your store procedure and use set this value to it. use where you want.
The only problem is when the string already contains a period. One thing should be noted that PARSENAME only expects four parts, so using a string with more than four parts causes it to return NULL
Try this function:
CREATE FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
And use it like this:
select * from dbo.fnSplitString('Querying SQL Server','')
This will scale to any number of search terms and does not require dynamic SQL.
declare #a table (w varchar(50)) -- holds original string
declare #b table (s varchar(50)) -- holds parsed string
insert into #a -- load string into temp table
values ('ABC DataServices Pvt Ltd')
--parse string as XML
;WITH Vals AS (
SELECT w,
CAST('<d>' + REPLACE(w, ' ', '</d><d>') + '</d>' AS XML) XmlColumn
FROM #a
)
--Insert results to parsed string
insert into #b
SELECT
C.value('.','varchar(max)') ColumnValue
FROM Vals
CROSS APPLY Vals.XmlColumn.nodes('/d') AS T(C)
--Join on results
select * from companytable
join #b b on b.s like '%'+companyname+'%'
You can also try this.
First in your parameter's value replace space with comma (,) and then replace comma with desired where condition like this -
DECLARE #companyname VARCHAR(1000)
,#where VARCHAR(max)
SET #companyname = 'ABC DataServices Pvt Ltd'
SET #companyname = replace(#companyname, ' ', ', ')
SELECT #where = 'companyname like ''%' +
REPLACE(#companyname, ', ', '%'' OR companyname like ''%') + '%'''
PRINT #where
PRINT ('SELECT * FROM CompanyTable WHERE' + #where)
--EXEC ('SELECT * FROM CompanyTable WHERE' + #where)

Get single item from a list string in MySQL

Given the following strings which represent possible lists, how may I get an item at a specified index n
1,2,3,4,5
word1 word2 word3
pipe|delimited|list
Possible reasons for this functionality are
Extraction of specific elements from GROUP_CONCAT output
Extraction of specific elements from a SET column output (when cast to string)
Extraction of specific elements from a poorly normalised table containing a comma-separated list
Use within an iteration procedure to loop over a list and perform an action on each element within
There is no native function for this. You can use two SUBSTRING_INDEX functions. And you need to check if that specific index item exists:
SET #string:='1,2,3,4,5';
SET #delimiter:=',';
SET #n:=6;
SELECT
CASE WHEN
CHAR_LENGTH(#string)-CHAR_LENGTH(REPLACE(#string, #delimiter, ''))>=
#n*CHAR_LENGTH(#delimiter)-1
THEN
SUBSTRING_INDEX(SUBSTRING_INDEX(#string, #delimiter, #n), #delimiter, -1)
END;
SUBSTRING_INDEX(#string, #delimiter, #n) returns the substring from string #string before #n occurrences of the #delimiter.
SUBSTRING_INDEX( ... , #delimiter, -1) returns everything to the right of the final delimiter
you need to check if delimiter #n exists. We can substract the length of the string with the delimiter, and the string with the delimiter removed - using REPLACE(#string, #delimiter, '') - and see if it is greater than #n*CHAR_LENGTH(#delimiter)-1
Pure SQL way of doing it:-
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(somefield, '|', 3), '|', -1)
FROM sometable a
If you want to return NULL (or some other fixed value) if there is no 3rd element (for example):-
SELECT IF((LENGTH(somefield) - LENGTH(REPLACE(somefield, '|', '')) + 1) >= 10, SUBSTRING_INDEX(SUBSTRING_INDEX(somefield, '|', 10), '|', -1), NULL)
FROM sometable a
update totally forgot the SUBSTRING_INDEX -1 thing (pointed out by #fthiella and #Kickstart), so updated the examples below
Solved by creating a stored function which is able to be used in-line. The stored function is able to accept an input string, any single-character delimiter, and an index of the desired item to extract.
The stored function definition is as follows
CREATE DEFINER = `user`#`%`
FUNCTION `getListElement`(
inString VARCHAR(255) ,
inDelimiter CHAR(1) ,
inIndexToReturn TINYINT UNSIGNED
) RETURNS varchar(255) READS SQL DATA DETERMINISTIC SQL SECURITY INVOKER
BEGIN
-- Takes in as argument a string, and then breaks out the desired string
DECLARE resultString VARCHAR(255) DEFAULT inString;
DECLARE numberOfListElements TINYINT UNSIGNED DEFAULT 0;
DECLARE errorMessage VARCHAR(255) DEFAULT 'Requested index was < 1 which was invalid';
-- First of all, additional processing only needed for element 2 upwards
IF inIndexToReturn = 1 THEN
RETURN SUBSTRING_INDEX( resultString , inDelimiter , inIndexToReturn);
ELSEIF inIndexToReturn > 1 THEN
-- Count the number of elements
-- This will count the missing delimiters based off the replace. A list of 4 will be missing 3 characters.
SET numberOfListElements = ( CHAR_LENGTH( resultString ) + 1 ) - CHAR_LENGTH( REPLACE( resultString , inDelimiter , '' ) );
IF numberOfListElements >= inIndexToReturn THEN
-- Make sure to only return the last of the elements returend by the first SUBSTRING_INDEX
RETURN SUBSTRING_INDEX( SUBSTRING_INDEX( inString , inDelimiter , inIndexToReturn ) , inDelimiter , -1 );
END IF;
SET errorMessage = CONCAT('List index ',inIndexToReturn,' was requested from a list with ',numberOfListElements,' elements');
END IF;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
END
In the examples above, the following could be used to extract specific list elements
SELECT getListElement( '1,2,3,4,5' , ',' , 2 ) returns 2
SELECT getListElement( REPLACE( 'word1 word2 word3' ,' ', ',' ) , ',' , 3 ) returns word3 (see note below for reason on the REPLACE)
SELECT getListElement( 'pipe|delimited|list' , '|' , 1 ) returns pipe
It would also be possible to use this within an iterator to loop over elements in a list. First of all you need to count the items in the list (see How to count items in comma separated list MySQL) however once you have that, it's just a matter of iterating over them as in this fragment from a stored procedure
-- Reinitialise variables
SET #list = '1,2,3,4,5';
SET #delimiter = ',';
SET #listLength = (CHAR_LENGTH( #list ) + 1 ) - CHAR_LENGTH( REPLACE( #list , #delimiter , '' ) );
SET #currentElement = 1;
listLoop: REPEAT
-- Select the list element and do something with it
SELECT getListElement( #list , #delimiter , #currentElement );
-- Increment the current element
SET #currentElement = #currentElement + 1;
UNTIL #currentElement > #listLength
END REPEAT listLoop;
important space-delimited lists seem to cause issues for this procedure, and so prior to parsing a string into the function I would recommend doing a simple REPLACE to replace the spaces with another suitable single-character separator (i.e. , or | depending on the content in the string)

T-SQL strip all non-alpha and non-numeric characters

Is there a smarter way to remove all special characters rather than having a series of about 15 nested replace statements?
The following works, but only handles three characters (ampersand, blank and period).
select CustomerID, CustomerName,
Replace(Replace(Replace(CustomerName,'&',''),' ',''),'.','') as CustomerNameStripped
from Customer
One flexible-ish way;
CREATE FUNCTION [dbo].[fnRemovePatternFromString](#BUFFER VARCHAR(MAX), #PATTERN VARCHAR(128)) RETURNS VARCHAR(MAX) AS
BEGIN
DECLARE #POS INT = PATINDEX(#PATTERN, #BUFFER)
WHILE #POS > 0 BEGIN
SET #BUFFER = STUFF(#BUFFER, #POS, 1, '')
SET #POS = PATINDEX(#PATTERN, #BUFFER)
END
RETURN #BUFFER
END
select dbo.fnRemovePatternFromString('cake & beer $3.99!?c', '%[$&.!?]%')
(No column name)
cake beer 399c
Create a function:
CREATE FUNCTION dbo.StripNonAlphaNumerics
(
#s VARCHAR(255)
)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #p INT = 1, #n VARCHAR(255) = '';
WHILE #p <= LEN(#s)
BEGIN
IF SUBSTRING(#s, #p, 1) LIKE '[A-Za-z0-9]'
BEGIN
SET #n += SUBSTRING(#s, #p, 1);
END
SET #p += 1;
END
RETURN(#n);
END
GO
Then:
SELECT Result = dbo.StripNonAlphaNumerics
('My Customer''s dog & #1 friend are dope, yo!');
Results:
Result
------
MyCustomersdog1friendaredopeyo
To make it more flexible, you could pass in the pattern you want to allow:
CREATE FUNCTION dbo.StripNonAlphaNumerics
(
#s VARCHAR(255),
#pattern VARCHAR(255)
)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #p INT = 1, #n VARCHAR(255) = '';
WHILE #p <= LEN(#s)
BEGIN
IF SUBSTRING(#s, #p, 1) LIKE #pattern
BEGIN
SET #n += SUBSTRING(#s, #p, 1);
END
SET #p += 1;
END
RETURN(#n);
END
GO
Then:
SELECT r = dbo.StripNonAlphaNumerics
('Bob''s dog & #1 friend are dope, yo!', '[A-Za-z0-9]');
Results:
r
------
Bobsdog1friendaredopeyo
I faced this problem several years ago, so I wrote a SQL function to do the trick. Here is the original article (was used to scrape text out of HTML). I have since updated the function, as follows:
IF (object_id('dbo.fn_CleanString') IS NOT NULL)
BEGIN
PRINT 'Dropping: dbo.fn_CleanString'
DROP function dbo.fn_CleanString
END
GO
PRINT 'Creating: dbo.fn_CleanString'
GO
CREATE FUNCTION dbo.fn_CleanString
(
#string varchar(8000)
)
returns varchar(8000)
AS
BEGIN
---------------------------------------------------------------------------------------------------
-- Title: CleanString
-- Date Created: March 26, 2011
-- Author: William McEvoy
--
-- Description: This function removes special ascii characters from a string.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
declare #char char(1),
#len int,
#count int,
#newstring varchar(8000),
#replacement char(1)
select #count = 1,
#len = 0,
#newstring = '',
#replacement = ' '
---------------------------------------------------------------------------------------------------
-- M A I N P R O C E S S I N G
---------------------------------------------------------------------------------------------------
-- Remove Backspace characters
select #string = replace(#string,char(8),#replacement)
-- Remove Tabs
select #string = replace(#string,char(9),#replacement)
-- Remove line feed
select #string = replace(#string,char(10),#replacement)
-- Remove carriage return
select #string = replace(#string,char(13),#replacement)
-- Condense multiple spaces into a single space
-- This works by changing all double spaces to be OX where O = a space, and X = a special character
-- then all occurrences of XO are changed to O,
-- then all occurrences of X are changed to nothing, leaving just the O which is actually a single space
select #string = replace(replace(replace(ltrim(rtrim(#string)),' ', ' ' + char(7)),char(7)+' ',''),char(7),'')
-- Parse each character, remove non alpha-numeric
select #len = len(#string)
WHILE (#count <= #len)
BEGIN
-- Examine the character
select #char = substring(#string,#count,1)
IF (#char like '[a-z]') or (#char like '[A-Z]') or (#char like '[0-9]')
select #newstring = #newstring + #char
ELSE
select #newstring = #newstring + #replacement
select #count = #count + 1
END
return #newstring
END
GO
IF (object_id('dbo.fn_CleanString') IS NOT NULL)
PRINT 'Function created.'
ELSE
PRINT 'Function NOT created.'
GO
I know this is an old thread, but still, might be handy for others.
Here's a quick and dirty (Which I've done inversely - stripping out non-numerics) - using a recursive CTE.
What makes this one nice for me is that it's an inline function - so gets around the nasty RBAR effect of the usual scalar and table-valued functions.
Adjust your filter as needs be to include or exclude whatever char types.
Create Function fncV1_iStripAlphasFromData (
#iString Varchar(max)
)
Returns
Table With Schemabinding
As
Return(
with RawData as
(
Select #iString as iString
)
,
Anchor as
(
Select Case(IsNumeric (substring(iString, 1, 1))) when 1 then substring(iString, 1, 1) else '' End as oString, 2 as CharPos from RawData
UNION ALL
Select a.oString + Case(IsNumeric (substring(#iString, a.CharPos, 1))) when 1 then substring(#iString, a.CharPos, 1) else '' End, a.CharPos + 1
from RawData r
Inner Join Anchor a on a.CharPos <= len(rtrim(ltrim(#iString)))
)
Select top 1 oString from Anchor order by CharPos Desc
)
Go
select * from dbo.fncV1_iStripAlphasFromData ('00000')
select * from dbo.fncV1_iStripAlphasFromData ('00A00')
select * from dbo.fncV1_iStripAlphasFromData ('12345ABC6789!&*0')
If you can use SQL CLR you can use .NET regular expressions for this.
There is a third party (free) package that includes this and more - SQL Sharp .