I have a column that gives info on how to parse another column within the same table.The column has the property name and string length of the value that it appears in the next column. Here is how it looks:
PropertyNames PropertyValuesString
SP_PartnerCode:S:0:14:FirstName:S:14:5:LastName:S:19:9: InvestProBase2rogerpatterson
SP_PartnerCode:S:0:14:FirstName:S:14:5:LastName:S:19:7: InvestProBase2AaronSchmidt
SP_PartnerCode:S:0:0:FirstName:S:0:6:LastName:S:6:9: JosephGaultieri
SP_PartnerCode:S:0:14:FirstName:S:14:4:LastName:S:18:9: InvestProBase2ToddEdmondson
SP_PartnerCode:S:0:14:FirstName:S:14:7:LastName:S:21:4: InvestProBase2MichaelLove
I want to separate this into a column per property name, like this:
SP_PartnerCode FirstName LastName
InvestProBase2 roger patterson
InvestProBase2 Aaron Schmidt
Joseph Gaultieri
InvestProBase1 Kevin Lemmon
InvestProBase1 John Switzer
InvestProBase2 bryan abbott
InvestProBase2 Todd Edmondson
InvestProBase2 Michael Love
Is this possible?
You should really normalize your data, was the first thing that came to my mind.
This function parses your data for a given property:
create function Parse(#property nvarchar(4000),
#meta nvarchar(4000), #data nvarchar(4000))
returns nvarchar(4000)
as
begin
set #meta = N':' + #meta
declare #iproperty int, #itype int, #ibegin int, #ilength int, #iend int
set #iproperty = charindex(N':' + #property + N':', #meta)
if #iproperty = 0 return null
set #itype = charindex(N':', #meta, #iproperty + 1)
set #ibegin = charindex(N':', #meta, #itype + 1)
set #ilength = charindex(N':', #meta, #ibegin + 1)
set #iend = charindex(N':', #meta, #ilength + 1)
declare #sbegin nvarchar(5), #slength nvarchar(5)
set #sbegin = substring(#meta, #ibegin + 1, #ilength - #ibegin - 1)
set #slength = substring(#meta, #ilength + 1, #iend - #ilength - 1)
declare #begin int, #length int
set #begin = convert(int, #sbegin)
set #length = convert(int, #slength)
if #length = 0 return null
return substring(#data, #begin + 1, #length)
end
and then SELECT your data
select
dbo.Parse('SP_PartnerCode', PropertyNames, PropertyValuesString) as SP_PartnerCode,
dbo.Parse('FirstName', PropertyNames, PropertyValuesString) as FirstName,
dbo.Parse('LastName', PropertyNames, PropertyValuesString) as LastName
from MyTable
You can use Substring
Related
In a procedure in SQL Server 2008 I need to replace a substring between 2 identifiers. I do not know the complete strings to be replaced, or if the second identifier exists.
If the terminator identifier is not present, I would need the end of the string to be considered one. Also, the identifiers could be the same.
DECLARE #startIdenfier VARCHAR(10) = 'the'
DECLARE #endIdenfier VARCHAR(10) = 'the'
DECLARE #newString VARCHAR(20) = 'new string that'
Sample input / output:
'This is the old string that the process needs to be applied on.' ->
'This is the new string that the process needs to be applied on.'
'This is the old string' ->
'This is the new string that'
SET #endIdenfier = 'I'
'This is the old string that I don't like' ->
'This is the new string that I don't like''
It is a generic replace and I havn't been able to find the proper way to do it, since the REPLACE function does not accept indexes.
EDIT: This community is awesome. I am sorry I cannot select multiple accepted solutions, but I thank you all for your help. I'll try all solutions already posted (besides the already accepted one which I tested) and up-vote individually.
--Find the start index plus the length of the string found (plus one for the space)
SET #startIdx = CHARINDEX(#startIdentifier, #initialString, 1) + LEN(#startIdentifier) + 1
--Find the next occurrence of the end identifier (minus one for the space)
SET #endIdx = CHARINDEX(#endIdentifier, #initialString, #startIdx) - 1;
--end not found?
IF #endIdx = -1 SET #endIdx = LEN(#initialString) + 1;
--Use the STUFF function to remove the old chars from endindex-startindex, and insert the new string at the startindex
SET #results = STUFF(#initialString, #startIdx, #endIdx - #startIdx, #newString)
In full:
DECLARE #startIdenfier Varchar(10)
SET #startIdenfier = 'the'
DECLARE #endIdenfier Varchar(10)
SET #endIdenfier = 'the'
DECLARE #newString Varchar(100)
SET #newString = 'new string that'
DECLARE #initialString VARCHAR(256) = 'this is the old string that the process needs to be applied on';
DECLARE #startIdx INT;
SET #startIdx = CHARINDEX(#startIdenfier, #initialString, 1) + LEN(#startIdenfier) + 1;
DECLARE #endIdx INT;
SET #endIdx= CHARINDEX(#endIdenfier, #initialString, #startIdx) - 1;
IF #endIdx = -1 SET #endIdx = LEN(#initialString) + 1;
DECLARE #results VARCHAR(256);
SET #results = STUFF(#initialString, #startIdx, #endIdx - #startIdx, #newString);
SELECT #results
You could do something like this:
/*Declare necessary variables*/
DECLARE #startIndex INT
DECLARE #endIndex INT
DECLARE #startReplace INT
DECLARE #lengthReplace INT
DECLARE #replaceString VARCHAR(500)
/*Get the index of the start/end idenfier*/
SELECT #startIndex = CHARINDEX ( #startIdenfier , #originalString)
SELECT #endIndex = CHARINDEX ( #startIdenfier , #originalString, #startIndex+1)
/*In case the end idenfier doesn't exist*/
IF #endIndex = 0
SET #endIndex = LEN(#originalString) + 1
SET #startReplace = #startIndex + len(#startIdenfier)
SET #lengthReplace = #endIndex - #startReplace
SELECT STUFF(#originalString, #startReplace, #lengthReplace, #newString)
I think you will need to incorporate the PATINDEX function and create the pattern by using your start and end identifiers. That will satisfy the condition where both start and end identifiers are present...
DECLARE #OldString nvarchar(max)
DECLARE #NewString nvarchar(max)
DECLARE #StartLocation bigint
DECLARE #Pattern nvarchar(200) = '%' + #StartIdentifier + ' % ' + #EndIdentifer + '%'
SELECT #StartLocation = PATINDEX(#Pattern, 'old complete string')
If the pattern is located, then you can get the string to be replaced by substringing the 'old complete string', starting at position (#StartLocation + Length of #StartIdentifier + 1). To determine the length for SUBSTRING, you need to locate the position of #EndIdentifier, using CHARINDEX of the old complete string beginning at ( #StartLocation + Length of #StartIdentifier + 1). Subtract ( #StartLocation + Length of StartIdentifier + 1) from result of CHARINDEX.
SELECT #OldString = SUBSTRING('complete old string', #StartLocation + LEN(#StartIdentifier) + 1, CHARINDEX(' ' + #EndIdentifier, 'old complete string', #StartLocation + LEN(#StartIdentifier) + 1) - (#StartLocation + LEN(#StartIdentifier) + 1)))
You can at this point do a straight forward REPLACE to get the new string.
SELECT #NewCompleteString = REPLACE('old complete string', #OldString, #NewString)
"If there is no 'terminator' identifier, I would need the end of the
string to be considered one."
If the initial pattern was not located, we fall back to search for the #StartIdentifier only. For this you can reset the pattern to contain the #StartIdentifier only...
SELECT #Pattern = '%' + #StartIdentifier + ' %'
SELECT #StartLocation = PATINDEX(#Pattern, 'old complete string')
If the pattern is located then you can get the old string to replace by SUBSTRING starting at ( #StartLocation + Length of #StartIdentifier + 1 ), with a length of 'old complete string' length - ( #StartLocation + Length of #StartIdentifier + 1 )...
SELECT #OldString = SUBSTRING('old complete string', #StartLocation + LEN(#StartIdentifier) + 1, LEN('old complete string') - (#StartLocation + LEN(#StartIdentifier) + 1))
You can then REPLACE...
SELECT #NewCompleteString = REPLACE('old complete string', #OldString, #NewString)
Something like this...
DECLARE #initialString VARCHAR(32) = '1234567890123456789'
DECLARE #startIdentifier VARCHAR(32) = '34'
DECLARE #endIdentifier VARCHAR(32) = '34'
DECLARE #newString VARCHAR(32) = 'ABC'
DECLARE #headChars INT = CHARINDEX(#startIdentifier, #initialString, 1)
IF #headChars > 0
SET #headChars = #headChars + LEN(#startIdentifier) - 1
DECLARE #bodyChars INT = CHARINDEX(#endIdentifier, #initialString, #headChars + 1)
IF #bodyChars > 0
SET #bodyChars = LEN(#initialString) - #bodyChars + 1
SELECT
LEFT(#initialString, #headChars)
+ #newString
+ RIGHT(#initialString, #bodyChars)
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b8a179d0e63840dfa60905d9951e4b22
For example...
'1234567890123456789'
'34' => Start # 1 => Found # 3 => keep chars 1->4
'34' => Start # 5 => Found # 13 => keep chars 13->end
Just use STUFF and CHARINDEX. Figure out:
the position at which replacement begins (position of the + length of the)
the position at which replacement ends (position of the starting from see above)
And subtract the positions to calculate the number of characters to replace.
DECLARE #string VARCHAR(100) = 'This is the old string that the process needs to be applied on.'
DECLARE #replace VARCHAR(100) = 'NEW STRING THAT '
DECLARE #delim1 VARCHAR(100) = 'the '
DECLARE #delim2 VARCHAR(100) = 'the '
DECLARE #pos1 INT = CHARINDEX(#delim1, #string) + DATALENGTH(#delim1)
DECLARE #pos2 INT = ISNULL(NULLIF(CHARINDEX(#delim2, #string, #pos1), 0), DATALENGTH(#string) + 1)
SELECT STUFF(#string, #pos1, #pos2 - #pos1, #replace)
-- "This is the NEW STRING THAT the process needs to be applied on."
SET #delim2 = 'xxx'
SET #pos1 = CHARINDEX(#delim1, #string) + DATALENGTH(#delim1)
SET #pos2 = ISNULL(NULLIF(CHARINDEX(#delim2, #string, #pos1), 0), DATALENGTH(#string) + 1)
SELECT STUFF(#string, #pos1, #pos2 - #pos1, #replace)
-- "This is the NEW STRING THAT "
Note: spaces should be a part of search delimiters instead of the logic. the should not match them and threfore.
I'm looking for a query for HSQLDB.
I have a string that contains address information delimited by "/". Now I have to split this string by "/" and insert individual address information into separate columns.
Address = /1234/CLAREVIEW////WILMINGTON/DE/19702
This needs to be split as
StreetNo = Address[1] = 1234
StreetName = Address[2] = CLAREVIEW
StreetType = Address[3] =
City = Address[6] = WILMINGTON
StateCd = Address[7] = DE
ZipCd = Address[8] = 19702
How can i achieve this?
CREATE PROCEDURE with the REGEXP_SUBSTRING_ARRAY function to split into an array.
REGEXP_SUBSTRING_ARRAY('/1234/CLAREVIEW////WILMINGTON/DE/19702', '/\p{Alnum}*');
Returns
ARRAY['/1234','/CLAREVIEW','/','/','/','/WILMINGTON','/DE','/19702']
So the procedure should contain:
CREATE PROCEDURE INSERT_USING_REGEXP (p1 VARCHAR(500))
BEGIN ATOMIC
DECLARE arr VARCHAR(200) ARRAY;
SET arr = REGEXP_SUBSTRING_ARRAY(p1,'/\p{Alnum}*');
INSERT INTO thetable ((StreetNo, StreetName, StreetType...) VALUES ( arr[1], arr[2], arr[3], ...);
END;
Then
CALL INSERT_USING_REGEXP('/1234/CLAREVIEW////WILMINGTON/DE/19702');
CREATE TABLE #Results
(
Ordinal NUMERIC,
StringValue VARCHAR(MAX)
)
DECLARE #String VARCHAR(100),
#Delimiter VARCHAR(100)
SET #String = '/1234/CLAREVIEW////WILMINGTON/DE/19702'
SET #Delimiter = '/'
DECLARE #TempString VARCHAR(MAX) = #String,
#Ordinal INT = 0,
#CharIndex INT = 0
SET #CharIndex = CHARINDEX(#Delimiter, #TempString)
WHILE #CharIndex != 0
BEGIN
SET #Ordinal += 1
INSERT #Results
VALUES (#Ordinal, SUBSTRING(#TempString, 0, #CharIndex))
SET #TempString = SUBSTRING(#TempString, #CharIndex + 1, LEN(#TempString) - #CharIndex)
SET #CharIndex = CHARINDEX(#Delimiter, #TempString)
END
IF #TempString != ''
BEGIN
SET #Ordinal += 1
INSERT #Results
VALUES (#Ordinal, #TempString)
END
SELECT *
FROM #Results
I took this answer from here but it should do the trick
I have my table with Column MailText which has values like
1. <strong>abc</strong>:<description1> <strong>bcd</strong>:<description2>
2. <strong>efg</strong>:<description3> <strong>hgl</strong>:<description7>
Upon update I want values like
1. <strong>abc</strong>:<abc> <strong>bcd</strong>:<bcd>
2. <strong>efg</strong>:<efg> <strong>hgl</strong>:<hgl>
Please help with dynamic replacement that it would update all string within <strong> tag to <description>. <strong> tag may contain any values.
CREATE FUNCTION GetString
(
#s NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #trav NVARCHAR(2000) = #s,
#length INT,
#count INT = 1,
#startIndex INT = 0,
#endIndex INT = 0,
#replaceStartIndex INT = 0,
#repalceEndIndex INT = 0,
#replaceword NVARCHAR(2000),
#newWord NVARCHAR(2000)
SELECT #length = LEN(#Trav)
WHILE ((#count + #startIndex) <= #length)
BEGIN
SET #startIndex = CHARINDEX('<strong>', #trav, #startIndex) + LEN('<strong>')
IF (#startIndex > 8)
BEGIN
SET #endIndex = CHARINDEX('</strong>', #trav, #startIndex)
SET #newWord = SUBSTRING(#trav, #startIndex, (#endIndex - #startIndex))
SET #replaceStartIndex = CHARINDEX(':', #trav, #startIndex) + 2
SET #repalceEndIndex = CHARINDEX('>', #trav, #replaceStartIndex)
SET #replaceword = SUBSTRING(
#trav,
#replaceStartIndex,
(#repalceEndIndex - #replaceStartIndex)
)
--SELECT #replaceword as 'repword', #newWord as 'newword'
SET #trav = REPLACE (#trav, #replaceword, #newWord)
SET #count = #repalceEndIndex
END
ELSE
BEGIN
SET #count = #count + 1
END
END
RETURN #trav
END
GO
IF OBJECT_ID('tempdb..#table') IS NOT NULL
DROP TABLE #table
CREATE TABLE #table
(
string VARCHAR(1000)
)
INSERT INTO #table
SELECT
'1. <strong>abc</strong>:<description1> <strong>bcd</strong>:<description2>'
INSERT INTO #table
SELECT
'2. <strong>efg</strong>:<description3> <strong>hgl</strong>:<description7>'
UPDATE #table
SET string = [dbo].[GetString](#table.string)
SELECT *
FROM #table
have table
CREATE TABLE #tbl
(
id int identity(1,1),
obj_type int ,
obj_id nvarchar(50)
)
have data like : 153:0|114:0|147:0|148:0|152:0|155:0
want insert which data ise before " : " to obj_id , which data is next to " : " insert tu obj_type. it's must be like
id obj_type obj_id
1 0 153
2 0 114
3 0 147
4 0 148
5 0 152
6 0 155
How do it in stored procedure ? not function
declare #S varchar(100) = '153:0|114:0|147:0|148:0|152:0|155:0'
declare #xml xml
select #xml = '<item><value>'+replace(replace(#s, ':','</value><value>'), '|','</value></item><item><value>')+'</value></item>'
select N.value('value[1]', 'int') as obj_id,
N.value('value[2]', 'int') as obj_type
from #xml.nodes('item') as T(N)
SQL Fiddle
You can wait for some experts answer
till then you can give it one chance
insert into #tbl
SELECT LEFT(splitdata, CHARINDEX(':', splitdata) - 1) AS obj_id,
RIGHT(splitdata, CHARINDEX(':', REVERSE(splitdata)) - 1) AS obj_type from (select splitdatafrom fnSplitString(parameterName,'|')
now you can write stringsplit function like this
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
Another Solution :
Create FUNCTION [dbo].[SplitString]
(
#List NVARCHAR(MAX),
#Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT
[Value] = LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim, #List + #Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim + #List, [Number], LEN(#Delim)) = #Delim
) AS y
);
taken form: T-SQL split string
And then select the values:
Declare
#Text varchar (100) = '153:0|114:0|147:0|148:0|152:0|155:0',
#Delim varchar(50) = ':0|'
select case when CHARINDEX(':0', Value) > 0 then Left(Value, Len(Value)-2) else Value End AS Result from dbo.SplitString(#Text, #Delim)
CREATE procedure [dbo].[Insert_procedure]
#inputString varchar(max)
AS
BEGIN
set #inputString ='2153:770|114:0|147:0|148:0|152:0|155:0' Declare #delimiter char(1) = '|' Declare #delimiter_Colon char(1) = ':'
DECLARE #chIndex int DECLARE #chIndex1 int DECLARE #item varchar(100)Declare #ReverseString varchar(max)
SELECT #ReverseString = Reverse(substring(reverse(#inputString), 1, 1))
IF(#ReverseString <> '|')
set #inputString = #inputString +'|'
WHILE CHARINDEX(#delimiter, #inputString, 0) <> 0
BEGIN
SET #chIndex = CHARINDEX(#delimiter, #inputString, 0)
SELECT #item = SUBSTRING(#inputString, 1, #chIndex - 1)
IF LEN(#item) > 0
BEGIN
set #chIndex1 = CHARINDEX(#delimiter_Colon, #item, 0)
Declare #obj_type int Declare #obj_id varchar(50)
SELECT #obj_id = SUBSTRING(#item, #chIndex1+1,len(#item)) SELECT #obj_type = SUBSTRING(#item,1,#chIndex1-1)
Insert into TEST(obj_type,obj_id) values (#obj_type,#obj_id)
END
SELECT #inputString = SUBSTRING(#inputString, #chIndex + 1, LEN(#inputString))
END
END
I need to pull a specific substring from a string of the form:
foo=abc;bar=def;baz=ghi
For example, how would I get the value of "bar" from that string?
You can use charindex and substring. For example, to search for the value of "baz":
declare #str varchar(128)
set #str = 'foo=abc;bar=def;baz=ghi'
-- Make sure #str starts and ends with a ;
set #str = ';' + #str + ';'
select substring(#str,
charindex(';baz=',#str) + len(';baz='),
charindex('=',#str,charindex(';baz=',#str)) - charindex(';baz=',#str) - 1)
Or for the value of "foo" at the start of the string:
select substring(#str,
charindex(';foo=',#str) + len(';foo='),
charindex('=',#str,charindex(';foo=',#str)) - charindex(';foo=',#str) - 1)
Here's a UDF to accomplish this (more readable version inspired by BlackTigerX's answer):
create function dbo.FindValueInString(
#search varchar(256),
#name varchar(30))
returns varchar(30)
as
begin
declare #name_start int
declare #name_length int
declare #value_start int
declare #value_end int
set #search = ';' + #search
set #name_start = charindex(';' + #name + '=',#search)
if #name_start = 0
return NULL
set #name_length = len(';' + #name + '=')
set #value_start = #name_start + #name_length
set #value_end = charindex(';', #search, #value_start)
return substring(#search, #value_start, #value_end - #value_start)
end
As you can see, this isn't easy in Sql Server :) Better do this in the client language, or normalize your database so the substrings go in their own columns.
I have a generalized solution that works for this problem:
CREATE FUNCTION [dbo].[fn_StringBetween]
(
#BaseString varchar(max),
#StringDelim1 varchar(max),
#StringDelim2 varchar(max)
)
RETURNS varchar(max)
AS
BEGIN
DECLARE #at1 int
DECLARE #at2 int
DECLARE #rtrn varchar(max)
SET #at1 = CHARINDEX(#StringDelim1, #BaseString)
IF #at1 > 0
BEGIN
SET #rtrn = SUBSTRING(#BaseString, #at1
+ LEN(#StringDelim1), LEN(#BaseString) - #at1)
SET #at2 = CHARINDEX(#StringDelim2, #rtrn)
IF #at2 > 0
SET #rtrn = LEFT(#rtrn, #at2 - 1)
END
RETURN #rtrn
END
so if you run (just wrap your original string to be searched with ';' at beginning and end):
PRINT dbo.fn_StringBetween(';foo=abc;bar=def;baz=ghi;', ';bar=', ';')
you will get 'def' returned.
look into the PATINDEX function. It has wildcard matching which should help you..
you can use this function
alter function FindValue(#txt varchar(200), #find varchar(200))
returns varchar(200)
as
begin
declare
#firstPos int,
#lastPos int
select #firstPos = charindex(#find, #txt), #lastPos = charindex(';', #txt, #firstPos+5)
select #lastPos = len(#txt)+1 where #lastPos = 0
return substring(#txt, #firstPos+len(#find)+1, #lastPos-#firstPos-len(#find)-1)
end
select dbo.FindValue('foo=abc;bar=def;baz=ghi', 'bar')
update: was not using the length of #find
this is assuming that the string will have the same string format just substitute the column name for the 'foo=abc;bar=def;baz=ghi'
select substring('foo=abc;bar=def;baz=ghi',patindex('%bar=%','foo=abc;bar=def;baz=ghi')+4, len('foo=abc;bar=def;baz=ghi')-patindex('%;baz=%','foo=abc;bar=def;baz=ghi')-4)
This can achieve in simple way
DECLARE #str VARCHAR(30)
DECLARE #start INT
SET #str='foo=abc;bar=def;baz=ghi'
SET #start=CHARINDEX('bar',#str)
PRINT SUBSTRING(#str,#start,3)