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
Related
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:
Got a table that contains a field similar to the following
856655.460000000000000+0.000000000000000+2200121.020000000000000
164171.720000000000000+0.000000000000000+421637.020000000000000
0.000000000000000+0.000000000000000+0.000000000000000+0.000000000000000
103176.220000000000000+0.000000000000000+264984.210000000000000
What I need to do is extract the numeric fields and total them. There may be a different number of numeric fields within the column, but they'll all be separated by the '+' symbol
Any help would be appreciated
Try this. to get a better answer you should add more detail about how the table is structured.
DECLARE #val VARCHAR(MAX) = '856655.460000000000000+0.000000000000000+2200121.020000000000000+164171.720000000000000+0.000000000000000+421637.020000000000000+0.000000000000000+0.000000000000000+0.000000000000000+0.000000000000000+103176.220000000000000+0.000000000000000+264984.210000000000000';
DECLARE #newVal VARCHAR(MAX);
CREATE TABLE #Table
(
value DECIMAL(30, 15)
);
WHILE LEN(#val) > 0
BEGIN
IF(#val LIKE '%+%')
BEGIN
SET #newVal = LEFT(#val, CHARINDEX('+', #val));
INSERT INTO #table
VALUES
(
CONVERT( DECIMAL(30, 15), LEFT(#newVal, LEN(#newVal) - 1))
);
SET #val = SUBSTRING(#val, LEN(#newVal)+1, LEN(#Val)-LEN(#newVal));
END;
ELSE
BEGIN
INSERT INTO #Table
VALUES
(
CONVERT( DECIMAL(30, 15), REPLACE(#val, '+', ''))
);
SET #val = '';
END;
END;
SELECT * FROM #Table
SELECT sum(value) FROM #Table;
DROP TABLE #Table
Edit:
To retrofit this to work on a table you could add a cursor which loops through each row in your table, runs the above query and updates the table with the results from the sum. I'm sure there are better ways but if this is a one time cleanup, it should work. Cheers
Clearly you will have to modify this to fit your situation, but the basic concept is to transform your plus sign separated string into xml and then use the nodes method to break it apart.
IF OBJECT_ID('tempdb..#temp', 'U') IS NOT NULL DROP TABLE #temp;
declare #string varchar(250)
declare #xml xml
set #string = '856655.460000000000000+0.000000000000000+2200121.020000000000000'
set #xml = ('<r>' + REPLACE(#string,'+','</r><r>') + '</r>')
select t.v.value('r[1]', 'decimal(25,15)') as Value1,
t.v.value('r[2]', 'decimal(25,15)') as Value2,
t.v.value('r[3]', 'decimal(25,15)') as Value3,
t.v.value('r[4]', 'decimal(25,15)') as Value4
into #temp
from #xml.nodes('/') AS t(v)
select *
from #temp
select coalesce(Value1, 0) + coalesce(Value2, 0) +
coalesce(Value3, 0) + coalesce(Value4, 0) as 'Total'
from #temp
You will have to add more code to the query that selects into #temp for each potential value in your string. If you have a massive amount of possible numbers this may not scale.
Hope this helps you get to what you need.
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"}
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)
I've to modify a Stored Procedure which inserts the data passed as input parameter in CSV varchar(MAX) format,
but now i've to pass two lists to SP and have to insert it into the table
data which I pass is as following
lstid = 1,2,3,4,5 etc.
lstvalue = 10,20,22,35,60 etc.
here lstid maps to lstvalue, means lstid = 1's value will be 10, lstid = 2's value will be 20 and so on
what should I do to insert the record based on mapping
I am using a function to seprate the CSV value and than stores it in temptable, but it work for obly one column
function is same as here
http://www.sqlteam.com/Forums/topic.asp?TOPIC_ID=14185
If you are forced to do this in a stored procedure and your arrays are equal size you can join the two lists, split them, and then join on position (the number of elements in each array) to get the linked set you need.
The below example uses a number table, but you can replace that split operation with any.
-- if you dont have a number table:
/*
create table [dbo].[Number](n int not null primary key clustered);
insert into dbo.Number
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),
(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
*/
declare #lstid varchar(100) = '1,2,3,4,51',
#lstvalue varchar(100) = '10,20,22,35,60'
declare #Length tinyint,
#Input varchar(8000),
#Delimiter char(1)
-- sanity check
if len(#lstid)-len(replace(#lstid, ',', '')) <> len(#lstvalue)-len(replace(#lstvalue, ',', ''))
begin
raiserror('lists are not equal', 16, 1);
return;
end
--count the numbers of elements in one of the arrays
select #Length = len(#lstid)-len(replace(#lstid, ',', ''))+1;
--join the two arrays into one
select #Input = #lstid + ',' + #lstvalue;
set #Delimiter = ',';
;with cte (i,s)
as (
select row_number() over (order by n.n asc) [i],
substring(#Delimiter + #Input + #Delimiter, n.n + 1, charindex(#Delimiter, #Delimiter + #Input + #Delimiter, n.n + 1) - n.n - 1) [s]
from dbo.Number n
where n.n = charindex(#Delimiter, #Delimiter + #Input + #Delimiter, n.n) and
n.n <= len(#Delimiter + #Input)
)
select a.s, b.s
from cte a
join cte b on
a.i+#Length = b.i
order
by a.i;
return
Create a data table with the data in .net code and pass that to SP.
How to pass data table to Sp from .net
You can pass your parameter lists as XML.
The parameter:
<lst><id>1</id><value>10</value></lst>
<lst><id>2</id><value>20</value></lst>
<lst><id>3</id><value>22</value></lst>
<lst><id>4</id><value>35</value></lst>
<lst><id>5</id><value>60</value></lst>
and the procedure
create procedure AddXML
#XML xml
as
insert into YourTable(id, value)
select N.value('id[1]', 'int'),
N.value('value[1]', 'int')
from #XML.nodes('/lst') as T(N)