This question already has answers here:
Parsing a string SQL
(3 answers)
Closed 8 years ago.
I have a table in which one column is having data like as below:
column value='A,B,C,D,E,F
XA123,Name1,10/20,1.11,27-03-2014,414BJE
XA154,Name2,10/10,1.143,26-03-2014,414B32
XA134,Name21,10/50,1.123,27-03-2014,414B534E
XA125,Name32,20/20,1.1234,17-02-2014,414BJ3
XA124,Name43,30/20,1.165,23-02-2014,414B432
XA1256,Name324,50/60,4.31,07-01-2014,4GHH
XA1252,Name32,70/60,6.61,09-12-2013,414B2E'
Now i need this column value to be sorted out like that in a separate table.
Expected Output:
A B C D E F
XA123 Name1 10/20 1.11 27-03-2014 414BJE
XA154 Name2 10/10 1.143 26-03-2014 414B32
XA134 Name21 10/50 1.123 27-03-2014 414B534E
XA125 Name32 20/20 1.1234 17-02-2014 414BJ3
XA124 Name43 30/20 1.165 23-02-2014 414B432
XA1256 Name324 50/60 4.31 07-01-2014 4GHH
XA1252 Name32 70/60 6.61 09-12-2013 414B2E
Edit:
To get the above solution, at first, i divided column value based on new line character and inserted into a new table variable:
Declare #value nvarchar(max) ='A,B,C,D,E,F
XA123,Name1,10/20,1.11,27-03-2014,414BJE
XA154,Name2,10/10,1.143,26-03-2014,414B32
XA134,Name21,10/50,1.123,27-03-2014,414B534E
XA125,Name32,20/20,1.1234,17-02-2014,414BJ3
XA124,Name43,30/20,1.165,23-02-2014,414B432
XA1256,Name324,50/60,4.31,07-01-2014,4GHH
XA1252,Name32,70/60,6.61,09-12-2013,414B2E'
Declare #t Table
(
Id int identity(1,1),
Val VARCHAR(max)
)
while (charindex(char(13),#value)>0)
BEGIN
insert into #t (Val)
select substring(#value,1,charindex(char(13),#value))
set #value = (select substring(#value,charindex(char(13),#value)+1,len(#value)))
END
select * from #t
Then i created a new table variable and separated value for each row of #t table variable based on ','. Now hoping to get a generic and better solution.
Edited again:
Here I am trying to make a generic solution for this issue..lets say values in these rows can go to any number, for ex:
A,B,C,D,E,F could be
A,B,C,D,E,F,G
A,B,C,D,E,F,G,H
A,B,C,D,E,F,G,H....Z
so i am trying to get solution using dynamic query in below code, but getting an error: Must declare the scalar variable "#xml"
Declare #value nvarchar(max) ='A,B,C,D,E,F,G
XA123,Name1,10/20,1.11,27-03-2014,414BJE,afs
XA154,Name2,10/10,1.143,26-03-2014,414B32,ag
XA134,Name21,10/50,1.123,27-03-2014,414B534E,GSF
XA125,Name32,20/20,1.1234,17-02-2014,414BJ3,GG
XA124,Name43,30/20,1.165,23-02-2014,414B432,GS
XA1256,Name324,50/60,4.31,07-01-2014,4GHH,GS
XA1252,Name32,70/60,6.61,09-12-2013,414B2E,sg'
declare #query varchar(max)
declare #xml xml
declare #count int
declare #i int = 1
select #xml = '<item><value>'+replace(replace(#value, ',','</value><value>'), char(10),'</value></item><item><value>')+'</value></item>'
DECLARE #XmlTable TABLE (XmlResult XML)
INSERT INTO #XmlTable select #xml
set #count = (SELECT XmlResult.value('count(/item/value)', 'int')/XmlResult.value('count(/item)', 'int') FROM #XmlTable)
SET #query = 'select '
WHILE (#i <= #count)
BEGIN
IF(#i!=1)
BEGIN
set #query = #query + ', '
END
set #query = #query + 'N.value(''substring(value['+ cast(#i as varchar) +'],1)'',''varchar(10)'')'
SET #i = #i + 1
END
set #query = #query + ' from ' + '#xml.nodes' + '(''item'') as T(N)'
-- select #query
EXEC(#query)
Guys, any suggestion...
Here it is...
Declare #value nvarchar(max) ='A,B,C,D,E,F
XA123,Name1,10/20,1.11,27-03-2014,414BJE
XA154,Name2,10/10,1.143,26-03-2014,414B32
XA134,Name21,10/50,1.123,27-03-2014,414B534E
XA125,Name32,20/20,1.1234,17-02-2014,414BJ3
XA124,Name43,30/20,1.165,23-02-2014,414B432
XA1256,Name324,50/60,4.31,07-01-2014,4GHH
XA1252,Name32,70/60,6.61,09-12-2013,414B2E'
declare #xml xml
select #xml = '<item><value>'+replace(replace(#value, ',','</value><value>'), char(13),'</value></item><item><value>')+'</value></item>'
select
N.value('substring(value[1],1)', 'varchar(10)') as V1,
N.value('substring(value[2],1)', 'varchar(10)') as V2,
N.value('substring(value[3],1)', 'varchar(10)') as V3,
N.value('substring(value[4],1)', 'varchar(10)') as V4,
N.value('substring(value[5],1)', 'varchar(10)') as V5,
N.value('substring(value[6],1)', 'varchar(10)') as V6
from #xml.nodes('item') as T(N)
Try this, First Create a Scalar function AS
Create FUNCTION [dbo].[funcSplit_OneVal]
(
#param NVARCHAR(MAX),
#delimiter CHAR(1),
#nThVal int
)
RETURNS varchar(50)
--select dbo.funcSplit_OneVal('1,2,3,4',',',5)
AS
BEGIN
Declare #t TABLE (val NVARCHAR(MAX))
Declare #retVal varchar(50) = ''
SET #param += #delimiter
;WITH a AS
(
SELECT CAST(1 AS BIGINT) f,
CHARINDEX(#delimiter, #param) t,
1 seq
UNION ALL
SELECT t + 1,
CHARINDEX(#delimiter, #param, t + 1),
seq + 1
FROM a
WHERE CHARINDEX(#delimiter, #param, t + 1) > 0
)
INSERT #t
SELECT SUBSTRING(#param, f, t - f)
FROM a
OPTION(MAXRECURSION 0)
select top (#nThVal) #retVal =val from #t
RETURN #retVal
END
Then Execute following Script,
Declare #value nvarchar(max) ='A,B,C,D,E,F
XA123,Name1,10/20,1.11,27-03-2014,414BJE
XA154,Name2,10/10,1.143,26-03-2014,414B32
XA134,Name21,10/50,1.123,27-03-2014,414B534E
XA125,Name32,20/20,1.1234,17-02-2014,414BJ3
XA124,Name43,30/20,1.165,23-02-2014,414B432
XA1256,Name324,50/60,4.31,07-01-2014,4GHH
XA1252,Name32,70/60,6.61,09-12-2013,414B2E'
DECLARE #xml AS XML = CAST(('<X>'+REPLACE(#value,' ' ,'</X><X>')+'</X>') AS XML)
;With CTE as
(
SELECT C.value('.', 'varchar(250)') AS value
FROM #xml.nodes('X') as X(C)
)
select dbo.funcSplit_OneVal(value,',',1)
,dbo.funcSplit_OneVal(value,',',2)
,dbo.funcSplit_OneVal(value,',',3)
,dbo.funcSplit_OneVal(value,',',4)
,dbo.funcSplit_OneVal(value,',',5)
,dbo.funcSplit_OneVal(value,',',6)
from CTE
where len(value) > 0
If required then you can remove 'A,B,C... from variable & give alise in Last Select.
This is the scenario:
My app will have the following:
A listbox (The checkbox property enabled) that will display a list of Something.
The user will select from the listbox (multiselect) by using the checkbox.
I will loop into All the checked items and store the ID's into an array. I will store the ID's into something like this separating the ID with a comma (1,2,3,4) and then I will use length -1 to delete the last comma.
How can I convert the string 1,2,3,4 into an integer type of data if my stored procedure is like this?
Select * from tblSomething Where ID in (1,2,3,4)
You can use the following SQL function.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CommaSeparatedToString]
(
#psCSString VARCHAR(8000)
)
RETURNS #otTemp TABLE(sID VARCHAR(20))
AS
BEGIN
DECLARE #sTemp VARCHAR(50)
WHILE LEN(#psCSString) > 0
BEGIN
SET #sTemp = LEFT(#psCSString, ISNULL(NULLIF(CHARINDEX(',', #psCSString) - 1, -1),
LEN(#psCSString)))
SET #psCSString = SUBSTRING(#psCSString,ISNULL(NULLIF(CHARINDEX(',', #psCSString), 0),
LEN(#psCSString)) + 1, LEN(#psCSString))
INSERT INTO #otTemp VALUES (#sTemp)
END
RETURN
END
And call in your stored procedure like
Select * from tblSomething
Where ID in (SELECT * FROM CommaSeparatedToString('1,2,3,4'))
You can use the
SELECT CAST(MyVarcharCol AS INT) FROM Table
SELECT CONVERT(INT, MyVarcharCol) FROM Table
refer this link
http://msdn.microsoft.com/en-us/library/ms187928.aspx
You need to create dynamic query for this
e.g you are getting list of values in #values paramter so prepare and run the dynamic query like this
DECLARE #query NVARCHAR(500)
DECLARE #values VARCHAR(200)
SET #values='1,2'
SET #query =N'Select * from tblSomething Where ID in ( ' + #values + ')'
SELECT #query
EXEC #Query
Use this function to split the value:
CREATE FUNCTION [dbo].[udfSplitCSV]
(
#String varchar (max),
#Delimiter varchar (10) = ','
)
RETURNS #ValueTable TABLE ([Row] int IDENTITY(1,1), [Value] varchar(max), [Length] int, [Duplicate] int NULL)
BEGIN
DECLARE #NextString varchar(max)
DECLARE #Pos int
DECLARE #NextPos int
IF #String IS NULL RETURN
--Initialize
SET #NextString = ''
SET #String = #String + #Delimiter
--Get position of first Comma
SET #Pos = charindex(#Delimiter,#String)
SET #NextPos = 1
--Loop while there is still a comma in the String
WHILE (#Pos <> 0)
BEGIN
SET #NextString = RTrim(LTrim(SubString(#String,1,#Pos - 1)))
INSERT INTO #ValueTable ([Value], [Length]) VALUES (#NextString, Len(#NextString))
SET #String = SubString(#String,#Pos+1,Len(#String))
SET #NextPos = #Pos
SET #Pos = CharIndex(#Delimiter,#String)
END
UPDATE #ValueTable
SET [Duplicate] = X.Duplicate
FROM #ValueTable VT
INNER JOIN (Select [Row], [Value], Row_Number() OVER (Partition By [Value] ORDER BY [Value], [Row]) as Duplicate FROM #ValueTable) X
ON X.[Row] = VT.[Row]
RETURN
END
-- Select * from dbo.udfSplitCSV('a , c b,c, a', ',')
When you are storing a bunch of IDs into the array, store with single quote.
so it will be ('1','2','3').
Then you no need to covert IDs into integer.
I wish to do something like the following:
declare #FrameNumber nvarchar(20)
set #FrameNumber = '(p1, p2)'
select from myTable where c1 in #FrameNumber
What is the correct syntax for this?
(for note: I need to pass the value of #FrameNumber in as a parameter to the stored procedure... so I have to at least use the string "p1, p2")
would prefure and answer that was SQL 7 compatible, but SQL 2005 would be sufficient.
DECLARE #FrameNumbers TABLE (code NVARCHAR(20) PRIMARY KEY)
INSERT
INTO #framenumbers
VALUES ('p1')
INSERT
INTO #framenumbers
VALUES ('p2')
SELECT *
FROM mytable
WHERE c1 IN
(
SELECT code
FROM #framenumbers
)
CREATE FUNCTION [dbo].[func_ParseStringToTable] (#stringIN varchar(2000))
RETURNS #tOUT TABLE(RoomID int) AS
BEGIN
DECLARE #pos int
SET #pos=CHARINDEX(',',#StringIN)
WHILE #pos>0
BEGIN
INSERT #tOUT(RoomID) SELECT LEFT(#StringIN,CHARINDEX(',',#StringIN)-1)
SET #stringIN = SUBSTRING(#StringIN,CHARINDEX(',',#StringIN)+1,LEN(#StringIN))
SET #pos=CHARINDEX(',',#StringIN)
END
IF LEN(#StringIN)>0
BEGIN
INSERT #tOUT(RoomID) SELECT #StringIN
END
RETURN
END
usage...
SELECT * FROM table WHERE id IN (func_ParseStringToTable(#ids))
You could put load those values into a table variable, or you could use dynamic sql. Here are examples of each:
TABLE VARIABLE
DECLARE #FrameNumbers TABLE (
Frame NVARCHAR(20)
)
INSERT INTO #FrameNumbers (
Frame
)
SELECT 'p1'
UNION ALL SELECT 'p2'
option 1:
SELECT * FROM myTable WHERE c1 in (
SELECT Frame
FROM #FrameNumbers
)
option 2:
SELECT
m.*
FROM myTable m
INNER JOIN #FrameNumbers f ON f.Frame = m.c1
All that is fine, but this is my favorite:
DYNAMIC SQL
DECLARE
#FrameNumber nvarchar(20),
#sql nvarchar(max),
#ParamDef nvarchar(1000)
SET #FrameNumber = '(p1, p2)'
SET #sql = N'SELECT FROM myTable WHERE c1 IN ' + #FrameNumber
EXECUTE dbo.sp_ExecuteSQL #sql
I have another solution to do with split function,
DECLARE #FrameNumber NVARCHAR(20)
SET #FrameNumber = 'p1,p2'
SELECT * FROM MyTable WHERE ProductCode IN
(SELECT Value FROM fn_Split(#FrameNumber, ','))
OutPut:
Split Functions:
CREATE FUNCTION fn_Split (
#String VARCHAR(8000)
,#Delimiter CHAR(1)
)
RETURNS #temptable TABLE (Value VARCHAR(8000))
AS
BEGIN
DECLARE #idx INT
DECLARE #slice VARCHAR(8000)
SELECT #idx = 1
IF len(#String) < 1
OR #String IS NULL
RETURN
WHILE #idx != 0
BEGIN
SET #idx = charindex(#Delimiter, #String)
IF #idx != 0
SET #slice = left(#String, #idx - 1)
ELSE
SET #slice = #String
IF (len(#slice) > 0)
INSERT INTO #temptable (Value)
VALUES (#slice)
SET #String = right(#String, len(#String) - #idx)
IF len(#String) = 0
BREAK
END
RETURN
END
what version of SQL Server ?
If you are in 2008 you might be able to use table datatypes. Simplifies these things a lot.
If you are using Sql Server 2005+ have a look at this
--Split
DECLARE #textXML XML
DECLARE #data NVARCHAR(MAX),
#delimiter NVARCHAR(5)
SELECT #data = 'A,B,C',
#delimiter = ','
SELECT #textXML = CAST('<d>' + REPLACE(#data, #delimiter, '</d><d>') + '</d>' AS XML)
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM #textXML.nodes('/d') T(split)
You can you that as your in table to select from.
Have a look at XML Support in Microsoft SQL Server 2005
final solution:
DECLARE #FrameNumbers TABLE (FrameNumber NVARCHAR(20) PRIMARY KEY)
DECLARE #pos int
SET #pos=CHARINDEX(',',#FrameNumber)
WHILE #pos>0 BEGIN
INSERT #FrameNumbers SELECT LEFT(#FrameNumber,CHARINDEX(',',#FrameNumber)-1)
SET #FrameNumber = SUBSTRING(#FrameNumber,CHARINDEX(',',#FrameNumber)+1,LEN(#FrameNumber))
SET #pos=CHARINDEX(',',#FrameNumber)
END
IF LEN(#FrameNumber)>0 BEGIN
INSERT #FrameNumbers SELECT #FrameNumber
END
select from myTable where c1 in (select FrameNumber from #FrameNumbers)
thanks Quassnoi and Sam, this solution is just a combination of your solutions.