Apply WHILE logic on Row-by-Row processing with Cursor - sql-server

I have T-SQL script that is parsing the MDX expression. It looks as:
IF OBJECT_ID ( 'tempdb..#metrics' ) IS NOT NULL
DROP TABLE #metrics
CREATE TABLE #metrics (
Metric VARCHAR(255)
)
---
DECLARE #counter INT = 1
DECLARE #mdx VARCHAR(4000) = 'SELECT {[Measures].[One],[Measures].[Two],[Measures].[Three],[Measures].[Four]} DIMENSION, PROPERTIES OTHER'
DECLARE #startString INT
DECLARE #endString INT
DECLARE #metric VARCHAR(200)
WHILE (1=1)
BEGIN
-- loop data and process them
SET #startString = (SELECT PATINDEX('%[[]Measures%',#mdx))
SET #endString = (SELECT CHARINDEX(',',#mdx))
SET #metric = (SELECT SUBSTRING(#mdx, #startString, #endString - #startString))
IF #metric LIKE '%}%'
BEGIN
SET #metric = LEFT(#metric, CHARINDEX('}',#metric) - 1)
INSERT INTO #metrics ( Metric ) SELECT #metric
SET #mdx = REPLACE(#mdx, #metric, '')
END
ELSE
BEGIN
INSERT INTO #metrics ( Metric ) SELECT #metric
SET #metric = #metric + ','
SET #mdx = REPLACE(#mdx, #metric, '')
END
-- break while
IF #mdx NOT LIKE '%[[]Measures%,%'
BEGIN
BREAK;
END
END
---
SELECT * FROM #metrics
Now, I need to apply this on more rows, but did not figure out how. I tried it with cursor, but it never ends. How to loop the logic on the following rows?
DECLARE #srcTable TABLE (
ID INT
,textData VARCHAR(4000)
)
INSERT INTO #srcTable ( ID, textData ) ( 1, 'SELECT {[Measures].[One],[Measures].[Two],[Measures].[Three],[Measures].[Four]} DIMENSION, PROPERTIES OTHER' )
,(2, 'SELECT {[Measures].[Five],[Measures].[Six],[Measures].[Seven]} DIMENSION, PROPERTIES OTHER' )
Desired Result:
1 [Measures].[One]
1 [Measures].[Two]
1 [Measures].[Three]
1 [Measures].[Four]
2 [Measures].[Five]
2 [Measures].[Six]
2 [Measures].[Seven]

DECLARE #t TABLE (
ID INT,
Metric VARCHAR(255)
)
INSERT INTO #t
VALUES
(1, 'SELECT {[Measures].[One],[Measures].[Two],[Measures].[Three],[Measures].[Four]} DIMENSION, PROPERTIES OTHER'),
(2, 'SELECT {[Measures].[Five],[Measures].[Six],[Measures].[Seven]} DIMENSION, PROPERTIES OTHER')
SELECT r.ID, item = t.c.value('.', 'VARCHAR(255)')
FROM (
SELECT *, txml = CAST('<r>' + REPLACE(Metric, ',', '</r><r>') + '</r>' AS XML)
FROM (
SELECT ID, Metric = SUBSTRING(Metric, CHARINDEX('{',Metric) + 1, CHARINDEX('}',Metric) - CHARINDEX('{',Metric) - 1)
FROM #t
) t
) r
CROSS APPLY txml.nodes('/r') t(c)
Output -
ID item
----------- -----------------------
1 [Measures].[One]
1 [Measures].[Two]
1 [Measures].[Three]
1 [Measures].[Four]
2 [Measures].[Five]
2 [Measures].[Six]
2 [Measures].[Seven]

Related

count and remove individual values of a single column

I have a table and the values like this
create table items_table(url varchar(max),counttotal_urls int,countduplicate_urls int,Unique_urls varchar(max),
countUnique_urls int)
insert into items_table(url) values('ht,ha,hb,ha|hc|hy')
insert into items_table(url) values('ht,hb,hb|hb|hx|hx')
insert into items_table(url) values('hz,hy,hx,hm|hm,hy')
insert into items_table(url) values('hz,hy,hx,hm|hm,hy')
I need to replace ,h with |h , for this I will use replace(url,',h','|h')
I need to count total_urls present by considering the separation '|'
I want to check or count how may duplicate url's are present
I want unique_urls to be updated in the Unique_url's column by removing the duplicates
Finally I want to count the Unique_urls which will be updated in another column
Desired output:
This is a bit complicated. But I tried to achieve it in Set based methodology.
Your Schema:
CREATE TABLE #items_table (
id INT identity
,url VARCHAR(max)
,counttotal_urls INT
,countduplicate_urls INT
,Unique_urls VARCHAR(max)
,countUnique_urls INT
)
INSERT INTO #items_table (url)
VALUES ('ht,ha,hb,ha|hc|hy')
INSERT INTO #items_table (url)
VALUES ('ht,hb,hb|hb|hx|hx')
INSERT INTO #items_table (url)
VALUES ('hz,hy,hx,hm|hm,hy')
INSERT INTO #items_table (url)
VALUES ('hy,hx,hm|hm,hy')
I have used several CTE's and XML methods
;WITH CTE
AS (
SELECT url
,REPLACE(',' + url, ',h', '|h') AS url2
,CAST('<M>'
+ REPLACE(REPLACE(',' + url, ',h', '|h'), '|', '</M><M>')
+ '</M>' AS XML) AS XML_FLD
FROM #items_table
)
,CTE2
AS (
SELECT url
,SUM(CASE
WHEN SUBSTRING(url2, number, 1) > '|'
THEN 1
ELSE 0
END) / 2 AS counttotal_urls
FROM CTE C
CROSS APPLY (
SELECT *
FROM master.dbo.spt_values
WHERE type = 'P'
AND number BETWEEN 1
AND LEN(C.url2)
) CA
GROUP BY url
)
,CTE3
AS (
SELECT C2.url
,C2.counttotal_urls
,SPLITS.ABC.value('.', 'varchar(MAX)') DUP_URLS
FROM CTE2 C2
INNER JOIN CTE C ON C2.url = C.url
CROSS APPLY C.XML_FLD.nodes('/M') AS SPLITS(ABC)
)
SELECT url
,counttotal_urls
,counttotal_urls - (COUNT(DISTINCT DUP_URLS) - 1) AS countduplicate_urls
,STUFF((
SELECT DISTINCT '|' + DUP_URLS
FROM CTE3 C
WHERE C3.url = C.url
FOR XML PATH('')
), 1, 1, '') AS Unique_urls
FROM CTE3 C3
GROUP BY url
,counttotal_urls
Result will be
+-------------------+-----------------+---------------------+-----------------+
| url | counttotal_urls | countduplicate_urls | Unique_urls |
+-------------------+-----------------+---------------------+-----------------+
| ht,ha,hb,ha|hc|hy | 6 | 1 | |ha|hb|hc|ht|hy |
| ht,hb,hb|hb|hx|hx | 6 | 3 | |hb|ht|hx |
| hy,hx,hm|hm,hy | 5 | 2 | |hm|hx|hy |
| hz,hy,hx,hm|hm,hy | 6 | 2 | |hm|hx|hy|hz |
+-------------------+-----------------+---------------------+-----------------+
You will have to create a string split table value function (Found one at aspsnippets)
as below
CREATE FUNCTION ufn_SplitString
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Item)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
GO
Once the Function is in place the below code can be used to achieve the desired result
;WITH cte_OriginalTable(url) as
(
SELECT 'ht,ha,hb,ha|hc|hy' UNION ALL
SELECT 'ht,hb,hb|hb|hx|hx' UNION ALL
SELECT 'hz,hy,hx,hm|hm,hy' UNION ALL
SELECT 'hz,hy,hx,hm|hm,hy'
)
,cte_SaperaterFix AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID, replace(url, ',h', '|h') AS url
FROM cte_OriginalTable
)
,cte_Split as
(
SELECT o.*,
y.Item
FROM cte_SaperaterFix o
CROSS APPLY dbo.ufn_SplitString(o.url, '|') y
)
,cte_TotalCount AS
(
SELECT ID,COUNT(ID) AS counttotal_urls, COUNT(DISTINCT Item) AS Unique_urls, COUNT(ID) - COUNT(DISTINCT Item) AS countduplicate_urls
FROM cte_Split
GROUP BY ID
)
SELECT DISTINCT b.ID, b.url AS URLs, a.CountTotal_URLs, a.CountDuplicate_URLS, STUFF(( SELECT DISTINCT '|' + b1.Item AS [text()]
FROM cte_Split b1
WHERE
b.ID = b1.ID
FOR XML PATH('')
), 1, 1, '' ) AS Unique_URLs, a.Unique_URLs AS CountUnique_URLs
FROM cte_TotalCount a
JOIN cte_Split b
ON a.ID = b.ID

subquery with table value function not working

DECLARE #temp AS TABLE (id INT, NAME VARCHAR(20) )
DECLARE #str VARCHAR(20) = '1,2'
INSERT INTO #temp (id, NAME)
VALUES (1, ''), (2, ''), (2, '')
SELECT *
FROM #temp a
WHERE id IN ((SELECT String FROM dbo.FN_SplitStrings(#str,',')))
I'm getting the following error while running this
Conversion failed when converting the varchar value '1,2' to data type
int.
Code:
CREATE function [dbo].[FN_SplitStrings]
(
#StringToSplit varchar(8000),
#Separator varchar(128)
)
RETURN TABLE
AS
RETURN
with indices as
(
select
0 S, 1 E
union all
select
E, charindex(#Separator, #StringToSplit, E) + len(#Separator)
from
indices
where E > S
)
select
substring(#StringToSplit,S, case when E > len(#Separator)
then e-s-len(#Separator) else len(#StringToSplit) - s + 1 end) String ,
S StartIndex
from
indices
where
S > 0
Try this. This splitting can be used without a function
DECLARE #temp AS TABLE
(
id INT,
NAME VARCHAR(20)
)
DECLARE #str VARCHAR(20)='1,2'
INSERT INTO #temp
( id, NAME )
VALUES ( 1, '' ),
( 2, ''),
( 2, '')
SELECT * FROM #temp a
WHERE id IN
(
SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'KeyWords'
FROM
(
-- To change ',' to any other delimeter, just change ',' before '</M><M>' to your desired one
SELECT CAST ('<M>' + REPLACE(#str, ',', '</M><M>') + '</M>' AS XML) AS Data
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
)
Click here to view the result
EDIT:
You had some problems in function for splitting.
Function
ALTER FUNCTION dbo.FN_SplitStrings(#StringToSplit varchar(8000),#Separator char(1))
RETURNS table
AS
RETURN (
WITH splitter_cte AS (
SELECT CHARINDEX(#Separator, #StringToSplit) as pos, 0 as lastPos
UNION ALL
SELECT CHARINDEX(#Separator, #StringToSplit, pos + 1), pos
FROM splitter_cte
WHERE pos > 0
)
SELECT SUBSTRING(#StringToSplit, lastPos + 1,
case when pos = 0 then 80000
else pos - lastPos -1 end) as String
FROM splitter_cte
)
Query
DECLARE #temp AS TABLE (id INT, NAME VARCHAR(20) )
DECLARE #str VARCHAR(20) = '1,2'
INSERT INTO #temp (id, NAME)
VALUES (1, ''), (2, ''), (2, '')
SELECT *
FROM #temp a
WHERE id IN ((SELECT String FROM dbo.FN_SplitStrings(#str,',')))
Click here to view result
The reason is a mix in data types and the fact that your function is an inline table valued functions which means that it is embedded into the query before query optimization takes place.
If you remove where S > 0 from your function and execute it with 1,2, the result of the function is:
String
------
1,2
1
2
Notice the first row where the value is 1,2.
When the optimizer does its job with your query the comparison against the column id is done before the where clause of the function is evaluated. In that comparison you have an implicit cast to int and 1,2 can not be casted to an int.
To fix this you can make sure that the String column of your split function is always an int (and perhaps changing the name of the column in the process).
select
cast(substring(#StringToSplit,S, case when E > len(#Separator)
then e-s-len(#Separator)
else len(#StringToSplit) - s + 1
end) as int) String ,

How to sum data from string

Have table :
id name
1 A1=7|A5=1|A10=5|A20=12|A50=8
2 A1=10|A5=2|A10=10|A20=14|A50=4
3 A1=3|A5=3|A10=5|A20=12|A50=8
.
.
Want sum all A1,A5,A10,A20,A50
Response must be like :
A1=20|A5=6|A10=20|A20=38|A50=20
How to do it ?
I also upvoted comment about changing table design but there are situations when somebody cannot do this. So here is the solution for this case and it's not so difficult. We call XML for assistance as usual when need to process formatted strings in XML columns.
-- Prepare data for solution testing
DECLARE #srctable TABLE (
id INT,
name VARCHAR(999),
namexml XML
)
INSERT INTO #srctable
SELECT id, name, namexml FROM ( VALUES
(1, 'A1=7|A5=1|A10=5|A20=12|A50=8', null),
(2, 'A1=10|A5=2|A10=10|A20=14|A50=4', null),
(3, 'A1=3|A5=3|A10=5|A20=12|A50=8', null)
) v (id, name, namexml)
-- Transform source formatted string to XML string
UPDATE #srctable
SET namexml = CAST('<row><data ' + REPLACE(REPLACE(name, '|', '"/><data '), '=', '="') + '"/></row>' AS XML)
-- Final select from XML data
SELECT SUM(x.data.value('(#A1)[1]', 'INT')) AS SUMA1,
SUM(x.data.value('(#A5)[1]', 'INT')) AS SUMA5,
SUM(x.data.value('(#A10)[1]', 'INT')) AS SUMA10,
SUM(x.data.value('(#A20)[1]', 'INT')) AS SUMA20,
SUM(x.data.value('(#A50)[1]', 'INT')) AS SUMA50
FROM #srctable AS t
CROSS APPLY t.namexml.nodes('/row/data') x (data)
You need to format your resulting string in any way you wish.
Variant without xml:
--------------------------------------------------------------------------------
-- Prepare data for solution testing
DECLARE #srctable TABLE ( id INT
, NAME VARCHAR(999) )
INSERT INTO #srctable
VALUES ( 1, 'A1=7|A5=1|A10=5|A20=12|A50=8' )
, ( 2, 'A1=10|A5=2|A10=10|A20=14|A50=4' )
, ( 3, 'A1=3|A5=3|A10=5|A20=12|A50=8' )
--------------------------------------------------------------------------------
-- prepare temp table for using in split string
DECLARE #Tally TABLE ( N INT )
DECLARE #i AS INT = 1
WHILE #i != 1000
BEGIN
INSERT INTO #Tally ( N )
VALUES ( #i )
SET #i = #i + 1
END
--------------------------------------------------------------------------------
--final query
;WITH cte AS
(
SELECT id,
(CASE WHEN CHARINDEX('|', S.string) > 0
THEN left(S.string, CHARINDEX('|', S.string) - 1)
ELSE string END ) NAME
FROM #srctable AS E
INNER JOIN #Tally AS T ON SUBSTRING('|' + NAME, T.N, 1) = '|'
AND T.N <= LEN(NAME)
CROSS APPLY (SELECT String = (CASE WHEN T.N = 1
THEN LEFT(E.NAME, CHARINDEX('|', E.NAME) - 1)
ELSE SUBSTRING(E.NAME, T.N, 1000) END )
) AS S
)
SELECT LEFT(NAME, CHARINDEX('=', NAME) - 1) AS NAME,
SUM(convert(float,RIGHT(NAME, CHARINDEX('=', REVERSE(NAME)) - 1))) AS Value
FROM cte
GROUP BY LEFT(NAME, CHARINDEX('=', NAME) - 1)

grouping, splitting, and counting rows

given this table and data:
DECLARE #Table table (RowID int, RowCode char(1), RowValue int);set nocount on
INSERT #Table VALUES ( 6,'A',3757 )
INSERT #Table VALUES ( 5,'A',37827)
INSERT #Table VALUES (14,'A',48411)
INSERT #Table VALUES ( 1,'A',48386)
INSERT #Table VALUES (20,'A',48450)
INSERT #Table VALUES ( 7,'A',46155)
INSERT #Table VALUES (13,'A',721 )
INSERT #Table VALUES ( 2,'A',49335)
INSERT #Table VALUES (15,'A',4700 )
INSERT #Table VALUES (19,'A',64416)
INSERT #Table VALUES ( 8,'A',27246)
INSERT #Table VALUES (12,'B',54929)
INSERT #Table VALUES (16,'B',3872 )
INSERT #Table VALUES ( 3,'C',728 )
INSERT #Table VALUES (11,'C',1050 )
INSERT #Table VALUES ( 9,'C',3191 )
INSERT #Table VALUES (17,'C',866 )
INSERT #Table VALUES ( 4,'C',838 )
INSERT #Table VALUES (10,'D',550 )
INSERT #Table VALUES (18,'D',1434 );set nocount off
I need this:
VVVVVVVV
RowID RowCode RowValue RowChunk
----- ------- -------- --------
1 A 48386 1
2 A 49335 1
5 A 37827 1
6 A 3757 1
7 A 46155 1
8 A 27246 2
13 A 721 2
14 A 48411 2
15 A 4700 2
19 A 64416 2
20 A 48450 3
12 B 54929 4
16 B 3872 4
3 C 728 5
4 C 838 5
9 C 3191 5
11 C 1050 5
17 C 866 5
10 D 550 6
18 D 1434 6
RowChunk starts at 1 and is incremented by 1 for each RowCode change and/or when there have been 5 of the same RowCode values.
Basically my solution uses the same approach as yours, only with slightly different devices employed.
WITH NumberedRows AS (
SELECT
RowID,
RowCode,
RowValue,
CodeChunk = (ROW_NUMBER() OVER (PARTITION BY RowCode ORDER BY RowID) - 1) / 5
FROM #Table
)
SELECT
RowID,
RowCode,
RowValue,
RowChunk = DENSE_RANK() OVER (ORDER BY RowCode, CodeChunk)
FROM NumberedRows
I don't think there's an analysis function, or any reasonable combination of such, which will address this. You'll have to do it RBAR with a cursor or, slightly faster in my experience, a loop.
This example of looping assumes that RowID is unique. If RowID is not the clustered PK, this will be very slow, so if that's the case you'll want to create a temp table.
DECLARE #RowID INT = (SELECT MIN(RowID) FROM #Table)
DECLARE #MaxRowID INT = (SELECT MAX(RowID) FROM #Table)
DECLARE #RowCode CHAR(1)
DECLARE #LastRowCode CHAR(1)
DECLARE #RowValue INT
DECLARE #Chunk INT = 0
DECLARE #RecsThisChunk INT
DECLARE #Results TABLE (RowID INT NOT NULL PRIMARY KEY, RowCode CHAR(1) NOT NULL, RowValue INT NOT NULL, Chunk INT NOT NULL)
WHILE #RowID <= #MaxRowID
BEGIN
-- Handle gaps in RowID
IF NOT EXISTS (SELECT * FROM #Table WHERE RowID = #RowID) GOTO EndOfLoop
-- Load values for this record
SELECT #RowCode = RowCode, #RowValue = RowValue FROM #Table WHERE RowID = #RowID
IF #LastRowCode IS NULL OR #RowCode <> #LastRowCode OR #RecsThisChunk = 5
BEGIN
-- Start a new chunk
SET #Chunk = #Chunk + 1
SET #RecsThisChunk = 1
END
ELSE
BEGIN
-- Same chunk
SET #RecsThisChunk = #RecsThisChunk + 1
END
SET #LastRowCode = #RowCode
INSERT INTO #Results (RowID, RowCode, RowValue, Chunk) VALUES (#RowID, #RowCode, #RowValue, #Chunk)
EndOfLoop:
SET #RowID = #RowID + 1
END
SELECT * FROM #Results
You may have tweak this a bit for 2005, I use 2008 routinely and don't recall all the little differences.
FYI, the results you show don't quite match the sample data.
Hope this helps! The only alternative I see is a cursor, or handling this in the application layer.
this does the trick without a loop:
;WITH NumberedRows AS (
SELECT
r.RowID, r.RowCode, r.RowValue, CEILING(ROW_NUMBER() OVER(PARTITION BY r.RowCode ORDER BY r.RowCode,r.RowID)/5.0) AS CodeRowChunk
FROM #Table r
)
, AllChunks AS (
SELECT r.*,ROW_NUMBER() OVER(ORDER BY RowCode,CodeRowChunk) AS ChunkRowNumber
FROM (SELECT DISTINCT
RowCode, CodeRowChunk
FROM NumberedRows) r
)
SELECT
a.RowID, RowCode, a.RowValue,
(SELECT ChunkRowNumber FROM AllChunks c WHERE c.RowCode=a.RowCode and c.CodeRowChunk=a.CodeRowChunk) AS RowChunk
FROM NumberedRows a
This is the answer you are looking for :
create Table [table] (RowID int, RowCode char(1), RowValue int)
INSERT [Table] VALUES ( 6,'A',3757 )
INSERT [Table] VALUES ( 5,'A',37827)
INSERT [Table] VALUES (14,'A',48411)
INSERT [Table] VALUES ( 1,'A',48386)
INSERT [Table] VALUES (20,'A',48450)
INSERT [Table] VALUES ( 7,'A',46155)
INSERT [Table] VALUES (13,'A',721 )
INSERT [Table] VALUES ( 2,'A',49335)
INSERT [Table] VALUES (15,'A',4700 )
INSERT [Table] VALUES (19,'A',64416)
INSERT [Table] VALUES ( 8,'A',27246)
INSERT [Table] VALUES (12,'B',54929)
INSERT [Table] VALUES (16,'B',3872 )
INSERT [Table] VALUES ( 3,'C',728 )
INSERT [Table] VALUES (11,'C',1050 )
INSERT [Table] VALUES ( 9,'C',3191 )
INSERT [Table] VALUES (17,'C',866 )
INSERT [Table] VALUES ( 4,'C',838 )
INSERT [Table] VALUES (10,'D',550 )
INSERT [Table] VALUES (18,'D',1434 )
IF object_id('tempdb..#tempTable') IS NOT NULL
BEGIN
DROP TABLE #tempTable
END
CREATE TABLE #tempTable
(RowID int, RowCode char(1), RowValue int,RowChunk int)
INSERT INTO #tempTable
select RowID,RowCode,RowValue,null from [table]
declare #RowId int
declare #RowCode char(1)
declare #Count int
declare #CurrentCode char(1)
declare #CountCurrent int
set #Count=1
set #CurrentCode=1
set #CountCurrent=0
DECLARE contact_cursor CURSOR FOR
SELECT RowID,RowCode FROM [table]
OPEN contact_cursor
FETCH NEXT FROM contact_cursor into #RowId,#RowCode
set #CurrentCode=#RowCode
WHILE ##FETCH_STATUS = 0
BEGIN
if(#CurrentCode=#RowCode)
begin
if(#CountCurrent=5)
begin
set #CountCurrent=1
set #Count=#Count+1
update #tempTable set RowChunk=#Count where RowID=#RowID
end
else
begin
set #CountCurrent=#CountCurrent+1
update #tempTable set RowChunk=#Count where RowID=#RowID
end
end
else
begin
set #CurrentCode=#RowCode
set #CountCurrent=1
set #Count=#Count+1
update #tempTable set RowChunk=#Count where RowID=#RowID
end
FETCH NEXT FROM contact_cursor into #RowId,#RowCode
END
CLOSE contact_cursor
DEALLOCATE contact_cursor
select * from #tempTable
GO

SQL Server: How do I delimit this data?

declare #mydata nvarchar(4000)
set #mydata = '36|0, 77|5, 132|61'
I have this data that I need to get into a table. So for Row1 columnA would be 36 and columnB would be 0. For Row2 columnA would be 77 and columnB would be 5 etc.
What is the best way to do this?
Thanks
You need a split table-valued-function. There's plenty of examples on the web, e.g. http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648
CREATE FUNCTION dbo.Split
(
#RowData nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(100)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
go
declare #mydata nvarchar(4000)
set #mydata = '36|0, 77|5, 132|61'
select
rowid, [1] as col1, [2] as col2
from
(
select
Row.Id as rowid, Col.Id as colid, Col.Data
from dbo.Split(#mydata, ',') as Row
cross apply dbo.Split(Row.Data, '|') as Col
) d
pivot
(
min(d.data)
for d.colid in ([1], [2])
) pd
I just picked the first split function I found. I don't thnk it's the best one but it works for this eample.
Ths outputs:
rowi col1 col2
1 36 0
2 77 5
3 132 61
If the data is in a file, you should be able tp bcp or BULK INSERT specifying row and column terminators
Otherwise, you'll need a nested split function
Of course, you could also send the data to SQL Server as xml

Resources