Split string into three variables in SQL Server 2005 - sql-server

I have an string
declare #s varchar(100),
set #s='aaaa,bbbb,cccc'
declare #first varchar(100),
declare #second varchar(100),
declare #third varchar(100)
Now I need to split these strings into three variable holding there values like this
#first=aaaa
#second=bbbb
#third=cccc
If I am using the split function then I get the output like this
aaaa
bbbb
cccc
Is there any better way we can achieve this result? Any help would be appreciated.
I want this result into a variable because I need it for further processing
Thanks

Here's a quick and dirty hack, assuming that your input strings always follow that format.
DECLARE #s VARCHAR(100)
SET #s = 'aaaa,bbbb,cccc'
DECLARE #first VARCHAR(100)
DECLARE #second VARCHAR(100)
DECLARE #third VARCHAR(100)
SET #s = '<row>' + REPLACE(#s, ',', '</row><row>') + '</row>'
SELECT #first = CONVERT(XML, #s).value('(/row)[1]', 'varchar(100)')
, #second = CONVERT(XML, #s).value('(/row)[2]', 'varchar(100)')
, #third = CONVERT(XML, #s).value('(/row)[3]', 'varchar(100)')
SELECT #first
, #second
, #third

If the split function you're using returns the three values in a table, one option might be to use that output to insert into a table variable with an integer identity, and then pick which variable pairs with which identity value:
DECLARE #first VARCHAR(100)
DECLARE #second VARCHAR(100)
DECLARE #third VARCHAR(100)
declare #split_output table (block varchar(100) )
declare #split_identity table (in_order int identity(1,1), block varchar(100))
/* recreate output of split fn */
insert into #split_output
select 'aaaa'
union
select 'bbbb'
union
select 'cccc'
select * from #split_output
/* push split output into table with identity column */
insert into #split_identity (block)
select block from #split_output
select * from #split_identity
/* Use identity value to pick which row of the table goes with which variable */
set #first = (select block from #split_identity where in_order = 1)
set #second = (select block from #split_identity where in_order = 2)
set #third = (select block from #split_identity where in_order = 3)
select #first, #second, #third

declare #s varchar(100)
set #s='aaaa,bbbb,cccc'
declare #first varchar(100)
declare #second varchar(100)
declare #third varchar(100)
select #first = left(#s, T.C1-1),
#second = substring(#s, T.C1+1, T.C2-T.C1),
#third = stuff(#s, 1, T.C2+1, '')
from (select charindex(',', #s),
len(#s)-charindex(',', reverse(#s))) as T(C1, C2)
String Functions (Transact-SQL)

Related

SQL - Get specific element from an array

Suppose I have 2 variables that look like an array:
declare #code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare #value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
I need to find if #code contains 10490 (for example) and if it does, I need to find a corresponding value (by its index) in #value variable which would be Digital since 10490 is the 4th element in #code array and 4th element of #value array is Digital (note that the 2nd element of the #value array is NULL.
Disclaimer:
#code array will ALWAYS contain unique values. It's not possible to have more than 1 10490 for example.
#code array will always start and end with ','.
Number of elements in #code and #value will always be the same if you take 1st and last comma off the #code variable.
I cannot use functions or stored procedures, so everything needs to be done as part of 1 query.
I think you know, that this is a very bad design... If you can change this, you really should. But this can be solved:
declare #code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare #value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
--The query will cast both strings to a splittable XML
--The query('/x[text()]') will remove empty entries (leading and trailing comma)
--(...assuming there will never be an empty entry in #code)
--Then it will read a derived numbered list from both
--finally it will join both lists on their PartIndex
WITH Casted AS
(
SELECT CAST('<x>' + REPLACE(#code,',','</x><x>') + '</x>' AS XML).query('/x[text()]') AS CodeXml
,CAST('<x>' + REPLACE(#value,'~','</x><x>') + '</x>' AS XML) AS ValueXml
)
,CodeDerived AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
,x.value('text()[1]','nvarchar(max)') AS CodePart
FROM Casted
CROSS APPLY CodeXml.nodes('/x') A(x)
)
,ValueDerived AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
,x.value('text()[1]','nvarchar(max)') AS ValuePart
FROM Casted
CROSS APPLY ValueXml.nodes('/x') A(x)
)
SELECT cd.PartIndex
,CodePart
,ValuePart
FROM CodeDerived cd
INNER JOIN ValueDerived vd ON cd.PartIndex=vd.PartIndex
The result
inx CodePart ValuePart
1 10501 True
2 10203 NULL
3 10491 100000006
4 10490 Digital
5 10091 0
6 10253 0
7 10008 NULL
8 10020 1388.76
9 10570 Completed
10 10499 True
Just add a simple WHERE to reduce this to the one value you need.
Disclaimer: it is not guaranteed, that the numbering with ROW_NUMBER and ORDER BY (SELECT NULL) will ever return the correct sequence, but for a better chance you'd need SQL Server 2016+. For more details: read this link and the other contributions there
Here are two possibilities. In your case I would even try to merge it into one WHILE loop.
SQL Server 2016 and above
(compatibility level 130 and up) you can use built in function STRING_SPLIT
DECLARE #code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
DECLARE #value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
DECLARE #valuetosearch nvarchar(200) = '10490'
SELECT value FROM
(
SELECT value ,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'idx'
FROM STRING_SPLIT ( #value , '~' )
) AS x2
WHERE x2.idx =
(
SELECT idx-1 FROM
(
SELECT value ,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'idx'
FROM STRING_SPLIT ( #code , ',' )
) AS x1
WHERE x1.[value] = #valuetosearch
)
For earlier versions of SQL Server:
DECLARE #code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
DECLARE #value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
DECLARE #valuetosearch nvarchar(200) = '10490'
DECLARE #codetbl AS TABLE (idx int IDENTITY(1,1)
,code nvarchar(200))
DECLARE #valuetbl AS TABLE (idx int IDENTITY(1,1)
,value nvarchar(200))
DECLARE #name nvarchar(200)
DECLARE #pos int
WHILE CHARINDEX(',', #code) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #code)
SELECT #name = SUBSTRING(#code, 1, #pos-1)
INSERT INTO #codetbl
SELECT #name
SELECT #code = SUBSTRING(#code, #pos+1, LEN(#code)-#pos)
END
INSERT INTO #codetbl
SELECT #code
WHILE CHARINDEX('~', #value) > 0
BEGIN
SELECT #pos = CHARINDEX('~', #value)
SELECT #name = SUBSTRING(#value, 1, #pos-1)
INSERT INTO #valuetbl
SELECT #name
SELECT #value = SUBSTRING(#value, #pos+1, LEN(#value)-#pos)
END
INSERT INTO #valuetbl
SELECT #value
SELECT value FROM #valuetbl
WHERE idx = (SELECT idx-1 FROM #codetbl WHERE code = #valuetosearch)
You may need to add some code for when #tofind is not found
declare #code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare #value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
declare #tofind nvarchar(200) = '10490';
--select left(#code,CHARINDEX(#tofind,#code))
--select len(left(#code,CHARINDEX(#tofind,#code))) - LEN( REPLACE( left(#code,CHARINDEX(#tofind,#code)) , ',', ''))
declare #nth int;
set #nth = len(left(#code,CHARINDEX(#tofind,#code))) - LEN( REPLACE( left(#code,CHARINDEX(#tofind,#code)) , ',', ''))
declare #SplitOn nvarchar = '~';
declare #RowData nvarchar(200) = #value + '~';
declare #Cnt int = 1
While (Charindex(#SplitOn,#RowData)>0) and #Cnt < #nth
Begin
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Select --Data = ltrim(rtrim(#RowData)),
Case when ltrim(rtrim(#RowData)) = '' then null else
LEFT(ltrim(rtrim(#RowData)) , CHARINDEX('~',ltrim(rtrim(#RowData))) -1)
end as Result
This should be quite simple. If performance is important I would suggest splitting the strings using DelimitedSplit8K. Here's a simple, high-performing solution:
DECLARE #searchFor INT = 10490;
SELECT code = s1.item, s2.item
FROM dbo.DelimitedSplit8K(#code,',') s1
JOIN dbo.DelimitedSplit8K(#value,'~') s2 ON s2.ItemNumber = s1.ItemNumber-1
WHERE s1.Item = #searchFor;
Results:
code item
---------- ------------
10490 Digital

"Error converting data type nvarchar to bigint" when executing a stored procedure

I'm trying to execute a stored procedure which inputs 3 parameters selected from a query. The first 2 stored procedure parameters are supposed to be int or bigint, but sql doesn't accept it and tell me it cannot convert type nvarchar to bigint.
So I changed the parameter types to nvarchar but now I get this error when executing a query within the stored procedure. I tried to convert nvarchar to bigint but it doesn't work even though the parameter values are numeric.
Here's how I'm executing the stored procedure:
[dbo].[InsertMultiChoiceList] [PatientRiskAssessmentQuestionsID], NetworkRiskAssessmentQuestionsID, Answer
The parameters being passed on look like these:
230124| 118 |COPD (Chronic Obstructive Pulmonary Disease), Congestive Heart Failure (CHF), Sleep Apnea
Here the definition of my stored procedure:
ALTER PROCEDURE [dbo].[InsertMultiChoiceList]
#PatientRiskAssessmentQuestionsID nvarchar(100),
#NetworkRiskAssessmentQuestionsID nvarchar(100),
#answer varchar(max)
AS
BEGIN
DECLARE #XML AS XML
DECLARE #Delimiter AS CHAR(1) =','
SET #XML = CAST(('<X>'+REPLACE(#answer , #Delimiter ,'</X><X>')+'</X>') AS XML)
DECLARE #temp TABLE (Answer Varchar(max))
INSERT INTO #temp
SELECT N.value('.', 'Varchar(max)') AS Answer
FROM #XML.nodes('X') AS T(N)
INSERT INTO [dbo].[PatientRiskAssessmentQuestionsList](NetworkRiskAssessmentListID, PatientRiskAssessmentQuestionsID)
SELECT
[dbo].[fnc_GetNetworkRiskAssessmentList](LTRIM(RTRIM(q.Answer)), #NetworkRiskAssessmentQuestionsID, 'List') AS NetworkRiskAssessmentListID,
#PatientRiskAssessmentQuestionsID
FROM
(SELECT Answer FROM #temp) q
WHERE
NOT EXISTS (SELECT 1
FROM PatientRiskAssessmentQuestionsList x
WHERE x.NetworkRiskAssessmentListID = NetworkRiskAssessmentListID
AND x.PatientRiskAssessmentQuestionsID = #PatientRiskAssessmentQuestionsID);
END
Here's the structure of the PatientRiskAssessmentQuestionsList table
Here's the script for fnc_GetNetworkRiskAssessmentList
ALTER function [dbo].[fnc_GetNetworkRiskAssessmentList]
(#text varchar(max),
#networkriskquestionid bigint,
#type varchar(20)
)
RETURNS BIGINT
AS
BEGIN
declare #id bigint
declare #questionid bigint
declare #count int
set #id = null
set #questionid = null
set #count = 0
if(#type = 'List')
begin
select #count = Count(*)
from NetworkRiskAssessmentList mc
where mc.Answer = #text
and mc.NetworkRiskAssessmentQuestionsID = #networkriskquestionid
if #count > 0
begin
select top(1) #questionid = mc.NetworkRiskAssessmentListID
from NetworkRiskAssessmentList mc
where mc.Answer = #text
and mc.NetworkRiskAssessmentQuestionsID = #networkriskquestionid
set #id = #questionid
end
end
return #questionid
end
The issue is you are trying to push a string value into the column NetworkRiskAssessmentListID which is actually BIGINT thus SQL doesn't allow the conversion.
Just a sample code to show you the issue
CREATE TABLE #Test
(
Patient BIGINT,
Network BIGINT
)
GO
DECLARE #Patient NVARCHAR(100)
DECLARE #Network NVARCHAR(100)
SET #Patient = '1234'
SET #Network = 'List'
INSERT INTO #Test VALUES (#Patient,#Network)
Hope this helps, try changing the datatype of the table and give it a try.
maybe your data has Enter character or Tab character so use code like below :
DECLARE #XML AS XML
DECLARE #Delimiter AS CHAR(1) =','
SET #XML = CAST(('<X>'+REPLACE(#answer , #Delimiter ,'</X><X>')+'</X>') AS XML)
DECLARE #temp TABLE (Answer Varchar(max))
INSERT INTO #temp
SELECT N.value('.', 'Varchar(max)') AS Answer FROM #XML.nodes('X') AS T(N)
insert into [dbo].[PatientRiskAssessmentQuestionsList](NetworkRiskAssessmentListID, PatientRiskAssessmentQuestionsID)
select
[dbo].[fnc_GetNetworkRiskAssessmentList](LTRIM(RTRIM(q.Answer)), #NetworkRiskAssessmentQuestionsID, 'List') as NetworkRiskAssessmentListID,
#PatientRiskAssessmentQuestionsID
from
(select cast(replace(replace(Answer, char(13), ''), char(10), '') as bigint) as Answer from #temp) q
where not exists
(
select 1 from PatientRiskAssessmentQuestionsList x
where x.NetworkRiskAssessmentListID = NetworkRiskAssessmentListID and x.PatientRiskAssessmentQuestionsID = #PatientRiskAssessmentQuestionsID
);
You are passing PatientRiskAssessmentQuestionsID value to #PatientRiskAssessmentQuestionsID nvarchar(100) parameter
Then you are using it in next code
select 1 from PatientRiskAssessmentQuestionsList x
where x.NetworkRiskAssessmentListID = NetworkRiskAssessmentListID
and x.PatientRiskAssessmentQuestionsID = #PatientRiskAssessmentQuestionsID
so the issue in the AND condition, here
and x.PatientRiskAssessmentQuestionsID = 'PatientRiskAssessmentQuestionsID'
x.PatientRiskAssessmentQuestionsID is bigint
and #PatientRiskAssessmentQuestionsID is nvarchar (100)
its value
[PatientRiskAssessmentQuestionsID]
so fix this one, and everything will be Ok.

Get Float value with Comma separtor

Am new to Sql server ce. my table have float value. I want select float with comma separated and decimal position. like this
table name table1
val
1220333
222
36535
I want result like this
val
12,20,333.00
222.00
36,535.00
like indian Rupees
Am using Sql server ce 3.5
DECLARE #Table1 TABLE
(val int)
;
INSERT INTO #Table1
(val)
VALUES
(1220333),
(222),
(36535)
;
select convert(varchar(50), CAST(val as money), -1) amount from #Table1
select FORMAT(CAST(val AS MONEY),'N','en-in') amount from #Table1
OR
Function
create function to_indian_currency(#n decimal(25,5))
returns varchar(100) as
BEGIN
declare #a varchar(100) = cast(#n as varchar(100))
declare #dec_part varchar(100) =
(select substring(#a, charindex('.',#a), len(#a)-charindex('.',#a)+1))
declare #int_part varchar(100) = (select left(#a, charindex('.',#a)-1))
declare #f int = cast(#int_part as bigint)%1000
declare #q int = cast(#int_part as bigint)/1000
declare #final varchar(100) = ''
while #q > 0
begin
set #final = cast(#q%100 as varchar) + ',' + #final
set #q = #q/100
end
RETURN #final + cast(#f as varchar) + #dec_part
END
select dbo.to_indian_currency(val) from #Table1

SQL server use "IF variable LIKE pattern"

Why this doesn't work?
DECLARE #str varchar = '######'
IF #str LIKE '%###%' SELECT 1
but this works
IF '######' LIKE '%###%' SELECT 1
UPDATE
why this works
DECLARE #Comment varchar(255) = '[A-B-C-D]'
IF #Comment LIKE '%[%-%-%-%]%' SELECT 1
however this doesn't work?
DECLARE #Comment nvarchar(255) = '[A-B-C-D]'
IF #Comment LIKE '%[%-%-%-%]%' SELECT 1
Add to your variable type length.
DECLARE #str varchar = '######'
IF #str LIKE '%###%' SELECT 1
is the same as (implicit cast will change it to '#')
DECLARE #str varchar(1) = '######'
/* IF '#' LIKE '%###%' SELECT 1 */
IF #str LIKE '%###%' SELECT 1
This will work:
DECLARE #str varchar(20) = '######'
IF #str LIKE '%###%' SELECT 1
try declaring the size of your varchar
DECLARE #str varchar(50) = '######'
SELECT 1 WHERE #str LIKE '%###%'
If you don't use specify type length, it will use mimimum code page length.This contants, varchar 1 and nvarchar 2. So, actually if scope is working normaly.
DECLARE #str varchar = '######'
SELECT DATALENGTH(#str)
DECLARE #str1 varchar(20) = '######'
select DATALENGTH(#str1)
DECLARE #str3 nvarchar = '######'
SELECT DATALENGTH(#str3)
DECLARE #str4 nvarchar(20) = '######'
select DATALENGTH(#str4)

Stored procedure to parse a string

I need to write a stored procedure for which the input is a string.
The input string contains variable names and their values separated by pipeline delimiter like this:
Name =Praveen | City=Hyderabad | Mobile=48629387429| Role=User| etc
In the stored procedure I have declared variables like #x, #y, #z, #t to obtain values as
#x=Praveen (Name value)
#y=Hyderabad (City Value)
#z=48629387429(Mobile Value)
#t=User(Role Value)
Also input string can have the values in any order like
City=Hyderabad | Mobile=48629387429 | Role=User | Name =Praveen |etc
Once I parse the values into #x, #y, #z, #t etc I have to use these values in the stored procedure.
Kindly let me how I can parse the input string to obtain the values of Name, City, Mobile, Role into #x, #y, #z and #t respectively.
One possible solution is use XML
DECLARE #text VARCHAR(1000)
,#xml xml
SELECT #text = 'City=Hyderabad | Mobile=48629387429 | Role=User | Name =Praveen'
SELECT #text = REPLACE(#text,'|','"')
,#text = REPLACE(#text,'=','="')
,#text = '<row ' + #text + '"/>'
SELECT #xml = CAST(#text AS XML)
select
line.col.value('#Name[1]', 'varchar(100)') AS Name
,line.col.value('#City[1]', 'varchar(100)') AS City
,line.col.value('#Mobile[1]', 'varchar(100)') AS Mobile
,line.col.value('#Role[1]', 'varchar(100)') AS Role
FROM #xml.nodes('/row') AS line(col)
I definitely recommend doing your string parsing on the program side as opposed to the data side. That being said, if you absolutely must you can try doing something similar to this:
DECLARE #String [nvarchar](256) = 'Name=Praveen | City=Hyderabad | Mobile=48629387429 | Role=User |'
DECLARE #name [nvarchar](256) = (SELECT SUBSTRING(#String, CHARINDEX('Name=', #String)+5, CHARINDEX('|', #String)))
DECLARE #city [nvarchar](256) = (SELECT SUBSTRING(#String, CHARINDEX('City=', #String)+5, CHARINDEX('|', #String)))
DECLARE #mobile [nvarchar](256) = (SELECT SUBSTRING(#String, CHARINDEX('Mobile=', #String)+7, CHARINDEX('|', #String)))
DECLARE #role [nvarchar](256) = (SELECT SUBSTRING(#String, CHARINDEX('Role=', #String)+5, CHARINDEX('|', #String)))
SELECT RTRIM(LTRIM(LEFT(#name, CHARINDEX('|', #name)-1))) AS Name,
RTRIM(LTRIM(LEFT(#city, CHARINDEX('|', #city)-1))) AS City,
RTRIM(LTRIM(LEFT(#mobile, CHARINDEX('|', #mobile)-1))) AS Mobile,
RTRIM(LTRIM(LEFT(#role, CHARINDEX('|', #role)-1))) AS Role
This returns:
Name | City | Mobile | Role
________________________________________________
Praveen | Hyderabad | 48629387429 | User
Note that the length being addedfrom the CHARINDEX in the initial queries are equal to the search string.
"Name=" is equal to 5 characters so we add 5 to move the index past the = sign,
"Mobile=" is equal to 7 so we add 7.
Similarly in the end SELECT query we are subtracting 1 from each CHARINDEX to remove the | symbol.
Sources:
SUBSTRING
CHARINDEX
LEFT
LTRIM
RTRIM
Let's assume your input param is called #Text.
DECLARE #Text varchar(255),
#x varchar(255)
SET #Text = 'Name=Praveen | City=Hyderabad | Mobile=48629387429| Role=User'
-- Added to show how to account for non-trailing |
SET #Text = #Text + ' | ';
SET #x = LTRIM(RTRIM(substring(
#Text,
charindex('Name=', #Text) + LEN('Name='),
charindex(' | ', #Text, charindex('Name=', #Text)) - LEN('Name=')
)))
SELECT #x
Then just repeat this for #y, #z, #t change Name= to whatever your break is.
Here's a fun way using a loop for string manipulation. Note how we are defining our #x, #y, etc. variable to grab a particular value.
-- Simulate proc parameter
declare #input nvarchar(max) = 'Name =Praveen | City=Hyderabad | Mobile=48629387429| Role=User'
-- OP's preferred destination vars
declare #x nvarchar(max) = 'Name'
declare #y nvarchar(max) = 'City'
declare #z nvarchar(max) = 'Mobile'
declare #t nvarchar(max) = 'Role'
-- The key/value delimiters we are expecting
declare #recordDelim nchar(1) = '|'
declare #valueDelim nchar(1) = '='
-- Temp storage
declare #inputTable table (
name nvarchar(128) not null primary key
, value nvarchar(max) null
)
-- Get all key/value pairs
while ltrim(rtrim(#input)) != '' begin
insert into #inputTable (name) select ltrim(rtrim(replace(left(#input, isnull(nullif(charindex(#recordDelim, #input), 0), len(#input))), #recordDelim, '')))
select #input = ltrim(rtrim(right(#input, len(#input) - isnull(nullif(charindex(#recordDelim, #input), 0), len(#input)))))
end
-- Separate keys and values
update #inputTable
set name = ltrim(rtrim(left(name, isnull(nullif(charindex(#valueDelim, name) - 1, 0), len(name)))))
, value = ltrim(rtrim(right(name, len(name) - isnull(nullif(charindex(#valueDelim, name), 0), len(name)))))
-- Populate the variables
-- If any are null, then this key/value wasn't present
set #x = (select value from #inputTable where name = #x)
set #y = (select value from #inputTable where name = #y)
set #z = (select value from #inputTable where name = #z)
set #t = (select value from #inputTable where name = #t)
Also, from the irregular spacing in your input, I'm guessing you want to trim everything coming in (which is why this proc does that all over the place).

Resources