"Error" value on name and description creating strange functionality - sql
I got a raw data file with its content looking like this:
MSN_Check,Text,25,MSN check
0,Text,1,(Result)
HWIMPL,Text,10,HWIMPL version reading
007F,Text,6,(Measure)
1,Text,1,(Result)
VHW,Text,10,FMT hardware version
494131383346,Text,10,(Measure)
0,Text,1,(Result)
TOTAL_VER,Text,25,Total version reading
313031303130,Text,6,(Measure)
1,Text,1,(Result)
CAL_MCU,Text,25,Total version reading
05,Text,6,(Measure)
Error,Text,25,Error
9.8499985089315E-07,Numeric,Float 3.3,(Measure)
CAL_EEPROM,Text,25,Total version reading
05,Numeric,Float 3.3,(Measure)
1,Text,1,(Result)
And I needed to extract and store in variables the name, example MSN_Check ,the description, example MSN check its result for example 0 and its measure , for example 007F but in some places I have results only or measures only so just spliting them wouldn't have helped.So my idea was:
First of all I created a template table named dbo.template that looks like this:
Name TestDescription Measure Result ID
----------------------------------------------
MSN_Check MSN check 0 1 1
HWIMPL HWIMPL version reading 1 1 2
VHW FMT hardware version 1 1 3
TOTAL_VER Total version reading 1 1 4
CAL_MCU Total version reading 1 0 5
Error Error 1 0 6
CAL_EEPROM Total version reading 1 1 7
In this table we have the name,description,if_measure(meaning 1 if we have a measure or 0 if we dont) and the if_result.And I made a query looking like this:
DECLARE #crlf AS CHAR(2) = CHAR(13) + CHAR(10)
declare #testname varchar(max),#testDescription varchar(max), #if_measure char(1), #if_result char(1), #row int = '1', #id int
set #LogEntry = (SELECT REPLACE(#LogEntry,#crlf,','))
declare #name varchar(max),#description varchar(MAX), #measure varchar(20), #result char(1)
declare #Output table(OutTestName varchar(max),OUTTestDescription varchar(max), OutMeasure varchar(50), OutResult varchar(50))
declare #maximum int = (select MAX(ID) from dbo.template_FMT)
declare #LogEntry1 as nvarchar(max)
declare #LogEntry2 as nvarchar(max)
while #row <= #maximum
BEGIN
set #name = null
set #description = null
set #measure = null
set #result = null
set #testname = (select Name from dbo.template_FMT where ID = #row)
set #testDescription = (select TestDescription from dbo.template_FMT where ID = #row)
set #if_measure = (select Measure from dbo.template_FMT where ID = #row)
set #if_result = (select Result from dbo.template_FMT where ID = #row)
set #id = (select ID from dbo.Split(#LogEntry, ',') where Data = #testname)
SELECT #LogEntry1 = Name FROM dbo.template_FMT where id = #row
set #name = #LogEntry1
SELECT #LogEntry2 = TestDescription FROM dbo.template_FMT where id = #row
set #description = #LogEntry2
if #if_measure > 0 and #if_result > 0
begin
set #measure = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+4)
set #result = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+8)
insert into #Output (OutTestName, OUTTestDescription, OutMeasure, OutResult) Values(#name,#description, #measure, #result)
end
if #if_measure > 0 and #if_result = 0
begin
set #measure = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+4)
set #result = null
insert into #Output (OutTestName, OUTTestDescription, OutMeasure, OutResult) Values(#name,#description, #measure, #result)
end
if #if_measure = 0 and #if_result > 0
begin
set #measure = null
set #result = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+4)
insert into #Output (OutTestName, OUTTestDescription, OutMeasure, OutResult) Values(#name,#description, #measure, #result)
end
set #row = #row + 1
END
select * from #Output
And it worked! but the only problem I have is where I have the row with the name Error with the description Error,it would return the last remembered value so instead of having
CAL_MCU Total version reading 05 NULL
Error Error 9.8499985089315E-07 NULL
CAL_EEPROM Total version reading 05 1
I get:
CAL_MCU Total version reading 05 NULL
Error Error 05 NULL
CAL_EEPROM Total version reading 05 1
And I would like to store the Error cant find Result with ID into variables if any of you have any suggestions :)
P.S. I think it has something to do with the fact that the name and description have the same name (Error)
I believe that your problem can be solved without the need for while loops and string splitting functions. I recommend using the OPENROWSET function to read your raw data file as a standard table. You can then use standard T-SQL query to format the result into the desired output.
The first step is to ensure that ad-hoc queries is enable on your server this can be accomplished by executing the following command.
sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
The next step is to define a format file for your text file. This will help SQL Server understand the text file structure when loading the raw data. Based on the supplied sample data your format file should look as follow:
10.0
4
1 SQLCHAR 0 100 "," 1 Col1 SQL_Latin1_General_CP1_CI_AS
2 SQLCHAR 0 100 "," 2 Col2 SQL_Latin1_General_CP1_CI_AS
3 SQLCHAR 0 100 "," 3 Col3 SQL_Latin1_General_CP1_CI_AS
4 SQLCHAR 0 100 "\r\n" 4 Col4 SQL_Latin1_General_CP1_CI_AS
I have uploaded the format file and example raw data file I have used to test the example at the following links:
http://www.filedropper.com/format
http://www.filedropper.com/rawdatafile
The final step is to run the OPENROWSET query to load the file data and transform the data to the desired output. If you are using SQL Server 2008 r2 then the following query should work:
-- 2008 R2 Version
WITH CTE_VariableRawData
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) AS ID
,[RawData].Col1 AS [VariableOrMeasure]
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 0
WHEN '(Measure)' THEN 0
ELSE 1
END
) AS IsVariable
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 1
ELSE 0
END
) AS IsResult
,(
CASE [RawData].Col4
WHEN '(Measure)' THEN 1
ELSE 0
END
) AS IsMeasure
,[RawData].Col4 AS [Description]
FROM OPENROWSET(BULK N'C:\temp\raw_data_file.txt', FORMATFILE = 'c:\temp\format.txt') AS [RawData]
)
,
CTE_RawDataByVariableID
AS
(
SELECT ID
,(
SELECT SUM([IsVariable])
FROM CTE_VariableRawData RunningTotal
WHERE RunningTotal.ID <= CTE_VariableRawData.ID
) AS VariableID
,[VariableOrMeasure]
,[IsVariable]
,[IsResult]
,[IsMeasure]
,[Description]
FROM CTE_VariableRawData
)
SELECT VariableID
,MAX(
CASE [IsVariable]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Variable]
,MAX(
CASE [IsVariable]
WHEN 1 THEN [Description]
ELSE NULL
END
) AS [Description]
,MAX(
CASE [IsMeasure]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Measure]
,MAX(
CASE [IsResult]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Result]
FROM CTE_RawDataByVariableID
GROUP BY VariableID
ORDER BY VariableID
If you are using SQL Server 2012 or later then the following query will be a bit more optimal:
WITH CTE_VariableRawData
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) AS ID
,[RawData].Col1 AS [VariableOrMeasure]
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 0
WHEN '(Measure)' THEN 0
ELSE 1
END
) AS IsVariable
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 1
ELSE 0
END
) AS IsResult
,(
CASE [RawData].Col4
WHEN '(Measure)' THEN 1
ELSE 0
END
) AS IsMeasure
,[RawData].Col4 AS [Description]
FROM OPENROWSET(BULK N'C:\temp\raw_data_file.txt', FORMATFILE = 'c:\temp\format.txt') AS [RawData]
)
,
CTE_RawDataByVariableID
AS
(
SELECT ID
,SUM([IsVariable]) OVER (ORDER BY ID) AS VariableID
,[VariableOrMeasure]
,[IsVariable]
,[IsResult]
,[IsMeasure]
,[Description]
FROM CTE_VariableRawData
)
SELECT VariableID
,MAX(
CASE [IsVariable]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Variable]
,MAX(
CASE [IsVariable]
WHEN 1 THEN [Description]
ELSE NULL
END
) AS [Description]
,MAX(
CASE [IsMeasure]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Measure]
,MAX(
CASE [IsResult]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Result]
FROM CTE_RawDataByVariableID
GROUP BY VariableID
ORDER BY VariableID;
Note that in both queries you will have to change the location of your raw data file and format file to the desired location within the OPENROWSET(BULK N'C:\temp\raw_data_file.txt', FORMATFILE = 'c:\temp\format.txt') call.
Related
How to generate row number based on certain condition?
I have column named type having values 1 and 2. i want to generate the expected results columns. In this column value 2 should be converted to 0 and for consecutive 1's it should generate the Row number. Please Refer this image... Any advise how can we achieve this. running out of logic.:(
Please try this : select *,0 as RowNo into #tmp from YourTable declare #id int set #id=0 update #tmp set #id = case typeId when 1 then #id+1 else 0 end, RowNo = case typeId when 1 then #id else 0 end select * from #tmp drop table #tmp
This is the best way to use Cursors CREATE TABLE [dbo].[Table_1]( [Type] [int] NULL) Code Declare #Type int = 0 DECLARE #Test TABLE( [Type] INT , [ExpectedResult] INT ) DECLARE vendor_cursor CURSOR FOR SELECT [Type] FROM [dbo].[Table_1] OPEN vendor_cursor FETCH NEXT FROM vendor_cursor INTO #Type Declare #ExpectedResult INT = 0 WHILE ##FETCH_STATUS = 0 BEGIN IF #Type = 2 SET #ExpectedResult = 0 ELSE SET #ExpectedResult+= 1 INSERT INTO #Test ([Type] ,[ExpectedResult] ) VALUES(#Type , #ExpectedResult) FETCH NEXT FROM vendor_cursor INTO #Type END CLOSE vendor_cursor; DEALLOCATE vendor_cursor; SELECT * FROM #Test
First, you are supposing that your rows have an ordering, but no ordering column is specified. SQL tables represent unordered sets. There is not ordering without such a column. Let me assume you have one. Then this is a gaps and islands problem. You want the islands of type = 1 so you can enumerate them. You can identify them by doing a cumulative sum of type = 2 -- this cumulative sum defines the grouping of adjacent type = 1 records. The rest is just row_number(): select t.*, (case when type = 2 then 0 else row_number() over (partition by type, grp order by <ordering col>) end) as expected_result from (select t.*, sum(case when type = 2 then 1 else 0 end) over (order by <ordering col>) as grp from t ) t; Here is a db<>fiddle.
A SQL query to count how many attributes a record has in common with another record?
I am trying to write a SQL query to take the count of columns with equal value in my schema for each row by comparison to a single record. Example: record1: 1, 0, 1, 0, 0 record2: 0, 0, 0, 0, 1 record3: 0, 0, 1, 0, 0 record1 has 2 attributes in common with record2, go through the entire table and order by number of attributes each record has in common with record1 Is there a way to write a SQL statement that will do this? I have only found ways to compare each row and specify which attributes must be of equal value.
You can do: select t.*, ((case when t.col1 = t1.col1 then 1 else 0 end) + (case when t.col2 = t1.col2 then 1 else 0 end) + (case when t.col3 = t1.col3 then 1 else 0 end) + . . . ) as num_in_common from t cross join t t1 where t1.id = 1; -- or however you define "record1" order by num_in_common desc;
Here's a nice routine you can use in SQL Server that will do this if you'd like. Replace #temp with your table name: declare #holding table (id int, col1 int, col2 int, col3 int, col4 int, col5 int, num_in_common int) declare #iterator int = 1 declare #row1col1 int, #row1col2 int, #row1col3 int, #row1col4 int ,#row1col5 int while #iterator<=(select max(id) from #temp) begin if #iterator=1 select #row1col1=col1, #row1col2=col2, #row1col3=col3, #row1col4=col4 ,#row1col5=col5 from #temp where id=#iterator else insert #holding select *, case when col1-#row1col1 = 0 then 1 else 0 end + case when col2-#row1col2 = 0 then 1 else 0 end + case when col3-#row1col3 = 0 then 1 else 0 end + case when col4-#row1col4 = 0 then 1 else 0 end + case when col5-#row1col5 = 0 then 1 else 0 end from #temp where id=#iterator set #iterator=#iterator+1 end select * from #holding
Incrementing Character value in T-sql
I have 2 set of values in a column i.e first 4 character are characters and next 4 character are numeric. Ex:AAAA1234 Now I have to increment the value from right end i.e when numeric value reached 9999 then I have to increment character by 1 character. Sample : Consider the last value stored in a column is AAAA9999 then next incremented values should be in a sequence AAAB9999,....... AABZ9999,..... BZZZ9999..... ZZZZ9999(last value). And when it reaches ZZZZ9999 then I have to reset the value to AAAA0001. How can do it in T-SQL ???
Here is a conceptual script, which does what you want. You will need to tweak it to suit your requirements DECLARE #test table(TestValue char(8)) DECLARE #CharPart char(4),#NumPart int SET #CharPart = 'AAAA' SET #NumPart = 1 WHILE #NumPart <=9999 BEGIN INSERT INTO #test SELECT #CharPart+RIGHT(('0000'+CAST(#NumPart AS varchar(4))),4) IF #NumPart = 9999 BEGIN IF SUBSTRING(#CharPart,4,1)<>'Z' BEGIN SET #CharPart = LEFT(#CharPart,3)+CHAR(ASCII(SUBSTRING(#CharPart,4,1))+1) SET #NumPart = 1 END ELSE IF SUBSTRING(#CharPart,4,1)='Z' AND SUBSTRING(#CharPart,3,1) <>'Z' BEGIN SET #CharPart = LEFT(#CharPart,2)+CHAR(ASCII(SUBSTRING(#CharPart,3,1))+1)+RIGHT(#CharPart,1) SET #NumPart = 1 END ELSE IF SUBSTRING(#CharPart,3,1)='Z' AND SUBSTRING(#CharPart,2,1) <>'Z' BEGIN SET #CharPart = LEFT(#CharPart,1)+CHAR(ASCII(SUBSTRING(#CharPart,2,1))+1)+RIGHT(#CharPart,2) SET #NumPart = 1 END ELSE IF SUBSTRING(#CharPart,1,1)<>'Z' BEGIN SET #CharPart = CHAR(ASCII(SUBSTRING(#CharPart,1,1))+1)+RIGHT(#CharPart,3) SET #NumPart = 1 END ELSE IF SUBSTRING(#CharPart,1,1)='Z' BEGIN SET #CharPart = 'AAAA' SET #NumPart = 1 INSERT INTO #test SELECT #CharPart+RIGHT(('0000'+CAST(#NumPart AS varchar(4))),4) BREAK END END ELSE BEGIN SET #NumPart=#NumPart+1 END END SELECT * FROM #test
With the help of PATINDEX,SUBSTRING,ASCII functions you can achieve your special cases. (I have found the solution for your special cases). Likewise you can add your own addition feature. create table #temp(col1 varchar(20)) insert into #temp values('AAAA9999') insert into #temp values('AAAZ9999') insert into #temp values('AAZZ9999') insert into #temp values('AZZZ9999') insert into #temp values('ZZZZ9999') select * from #temp select col1, case when cast(substring(col1,patindex('%[0-9]%',col1),len(col1)) as int) = 9999 and left(col1,4) <> 'ZZZZ' then case when substring(col1,(patindex('%[0-9]%',col1)-1),1) <> 'Z' then left(col1,3)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-1),1)) + 1)+right(col1,4) when substring(col1,(patindex('%[0-9]%',col1)-2),1) <> 'Z' then left(col1,2)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-2),1)) + 1)+right(col1,5) when substring(col1,(patindex('%[0-9]%',col1)-3),1) <> 'Z' then left(col1,1)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-3),1)) + 1)+right(col1,6) when substring(col1,(patindex('%[0-9]%',col1)-4),1) <> 'Z' then char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-4),1)) + 1)+right(col1,7) end else 'AAAA0001' end as outputofcol1 --patindex('%[0-9]%',col1)-1 as charpos, --substring(col1,(patindex('%[0-9]%',col1)-1),1) as substr4, --substring(col1,(patindex('%[0-9]%',col1)-2),1) as substr3, --substring(col1,(patindex('%[0-9]%',col1)-3),1) as substr2, --substring(col1,(patindex('%[0-9]%',col1)-4),1) as substr1 --ASCII(substring(col1,(patindex('%[0-9]%',col1)-1),1)) as ASC_value from #temp
The following function should return the desired value: IF OBJECT_ID (N'dbo.ufnGetIndexValue') IS NOT NULL DROP FUNCTION dbo.ufnGetIndexValue; GO CREATE FUNCTION dbo.ufnGetIndexValue(#MainString CHAR(8)) RETURNS CHAR(8) AS BEGIN DECLARE #NumberPart INT DECLARE #StringPart CHAR(4) DECLARE #Position TINYINT DECLARE #char CHAR SET #NumberPart=CONVERT(INT,SUBSTRING(#MainString,5,8)) SET #StringPart=SUBSTRING(#MainString,1,4) IF #NumberPart=9999 BEGIN SET #NumberPart=1111; SET #Position=4 WHILE #Position >= 1 BEGIN SET #char=SUBSTRING(#StringPart,#Position,1) IF(#char!='Z') BEGIN SET #char=CHAR(ASCII(#char)+1); SET #StringPart = STUFF(#StringPart,#Position,1,#char); BREAK; END SET #StringPart = STUFF(#StringPart,#Position,1,'A'); SET #Position-=1; END END ELSE BEGIN SET #NumberPart+=1; END SET #MainString=#StringPart+CAST(#NumberPart AS CHAR(4)); RETURN #MainString END GO
Here is a scalar select function that do the increment. CREATE FUNCTION dbo.inc_serial( #id char(8) ) RETURNS char(8) BEGIN select #id = case when SUBSTRING(id,2,1) <> '[' then id else STUFF( id, 1, 2, char(((ascii(id)+1-65)%26)+65) + 'A' ) end from ( select case when SUBSTRING(id,3,1) <> '[' then id else STUFF( id, 2, 2, char(ascii(right(id,7))+1) + 'A' ) end as id from ( select case when SUBSTRING(id,4,1) <> '[' then id else STUFF( id, 3, 2, char(ascii(right(id,6))+1) + 'A' ) end as id from ( select case when right(#id,4) < '9999' then concat( left(#id,4), right(concat( '000', (cast(right(#id,4) as smallint)+1) ), 4 ) ) else concat( left(#id,3), char(ascii(right(#id,5))+1), '0001' ) end as id ) t1 ) t2 ) t3 RETURN #id END Basically, the code just add one to the number, and repeatingly carring overflow up to the left. If your table always has one and only one row to be updated (e.g. an option/flag table): UPDATE [table] SET [serial] = dbo.inc_serial( [serial] ); If your table has multiple rows, you will need an identity or high precision creation time column, so that we know where to continue from after reset. INSERT INTO [table] (serial) VALUES ( dbo.inc_serial(( select top 1 case when count(*) > 0 then max([serial]) else 'AAAA0000' end AS id from [table] where [id] = ( select max([id]) from [table] ) ))); For concurrency safety, use XLOCK,ROWLOCK,HOLDLOCK to lock the table. They are obmitted from the examples for simplicity. If you do not like udf, you can embedded the query inline. An inline example for first case: UPDATE [table] SET [serial] = (( select case when SUBSTRING(id,2,1) <> '[' then id else STUFF( id, 1, 2, char(((ascii(id)+1-65)%26)+65) + 'A' ) end as id from ( select case when SUBSTRING(id,3,1) <> '[' then id else STUFF( id, 2, 2, char(ascii(right(id,7))+1) + 'A' ) end as id from ( select case when SUBSTRING(id,4,1) <> '[' then id else STUFF( id, 3, 2, char(ascii(right(id,6))+1) + 'A' ) end as id from ( select case when right(id,4) < '9999' then concat( left(id,4), right(concat( '000', (cast(right(id,4) as smallint)+1) ), 4 ) ) else concat( left(id,3), char(ascii(right(id,5))+1), '0001' ) end as id from ( select top 1 [serial] as id from [table] with (XLOCK,ROWLOCK,HOLDLOCK) ) t0 ) t1 ) t2 ) t3 )) The function can also be written as an inline table value function for better performance, at cost of more complex usage, but I would not border unless it frequently runs on multiple rows.
T-SQL Procedure split and join on sub columns
Not really sure how to explain this so I just start with the example. Say I have a table like this which can include several rows: id Type Text 1 Test Failure A=123 B=444 C=43343 Error=4 ErroDes=1 I also have a static Error and ErrorDes table which look like this Id Code Description 1 1 Error1 2 4 Error4 How can I split up the information from the column into seperate fields and also join in the info from the subtables. Expected result would be something like this: Type Field1 FieldA FieldB FieldC Error ErrorDes Test Failure 123 444 43343 Error4 Error1 I used the same table for joining in the example but this is 2 tables in the db. So to help with this I have a split function in the database. And if I first split the Text field on "space" and then on "=" I get everything I need (or atleast all the columns in seperate rows) cross apply dbo.Split(a.Text, ' ') s cross apply dbo.Split(s.Value, '=') s2 I get "TokenID" and "Value" field back from the split function. The output from that looks like this: TokenID Value TokenID Value 1 Failure 1 Failure 2 A=123 1 A 2 A=123 2 123 3 B=444 1 B 3 B=444 2 444 4 C=43343 1 C 4 C=43343 2 43343 5 Error=4 1 Error 5 Error=4 2 4 6 ErrorDes=1 1 ErrorDes 6 ErrorDes=1 2 1 I hope you understand what I ment and can help me how this can be solved.
you can use something like the folowing UDF function to cross apply create function udf_ReturnTextSplit(#vText varchar(100)) returns #rt table ( Field1 varchar(100), FieldA varchar(100), FieldB varchar(100) ) as begin declare #st varchar(100) = #vText + ' ' declare #sti varchar(100) declare #stj varchar(100) insert into #rt (Field1, FieldA, FieldB) values (null, null, null) declare #i int = charindex(' ', #st) while #i > 0 begin set #sti = SUBSTRING(#st, 1, #i) set #st = substring(#st, #i + 1, 100) set #i = CHARINDEX('=', #sti) if #i > 0 begin set #stj = substring(#sti, #i + 1, 100) set #sti = substring(#sti, 1, #i - 1) if #sti = 'A' update #rt set FieldA = #stj if #sti = 'B' update #rt set FieldB = #stj end else begin update #rt set Field1 = #sti end set #i = charindex(' ', #st) end return end go select * from dbo.udf_ReturnTextSplit('Failure A=123 B=444 C=43343 Error=4 ErroDes=1')
Need help to write a SQL query?
I have a SQL table with column which contain string like 'type|type1|type2|type3|type4'. I need to select the string, id. Split string and insert to another table. First item should be default and I need to get identity of it and insert to another table with the type value. Please help me to create T-SQL query to accomplish desirable result. Example Step 1 To select Items from Table 1. Step 2 Split to array Step 3 Insert to Table 2 (first item will be default) Step 4 Update Table 1 with default Type Value based on TypeID and Default True Table 1 ID Items Default -------------------------------------------------- 1 type|type1|type2|type3|type4 2 type|type1|type2|type3|type4 Table 2 ID TypeID Type Default(bool) -------------------------------------------------- 1 1 type1 1 2 1 type2 0
Using the split function from Arnold Fribble's answer on this thread Create FUNCTION dbo.Split (#sep char(1), #s varchar(512)) RETURNS table AS RETURN ( WITH Pieces(pn, start, stop) AS ( SELECT 1, 1, CHARINDEX(#sep, #s) UNION ALL SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1) FROM Pieces WHERE stop > 0 ) SELECT pn, SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s FROM Pieces ) GO You can write the following (I made some guesses about the ID field in table2 and what the defaultTypeID should be in table1 but you should be able to adjust that) CREATE TABLE #table1 ( id INT, items VARCHAR(MAX), defaulttypeid INT ) CREATE TABLE #table2 ( id INT IDENTITY, typeid INT, TYPE VARCHAR(5), isdefault BIT ) INSERT INTO #table1 VALUES (1, 'type|type1|type2|type3|type4', NULL), (2, 'type|type1|type2|type3|type4', NULL) INSERT INTO #table2 (typeid, TYPE, isdefault) SELECT id typeid, Rtrim(split.s) AS item, CASE WHEN ( split.pn = 1 ) THEN 1 ELSE 0 END AS isdefault FROM #table1 CROSS APPLY test.dbo.Split('|', items) AS split UPDATE #table1 SET defaulttypeid = t2.ID FROM #table1 t1 INNER JOIN #table2 t2 ON t1.id = t2.typeid AND t2.isdefault = 1 DROP TABLE #table1 DROP TABLE #table2 This outputs ID Items DefaultTypeID ----------- ------------------------------ ------------- 1 type|type1|type2|type3|type4 1 2 type|type1|type2|type3|type4 6 ID TypeID Type IsDefault ----------- ----------- ----- --------- 1 1 type 1 2 1 type1 0 3 1 type2 0 4 1 type3 0 5 1 type4 0 6 2 type 1 7 2 type1 0 8 2 type2 0 9 2 type3 0 10 2 type4 0
Though I totally disagree with the use of cursors I can't think of another way. This solution isn't tested but It looks like it should be ok. DECLARE #pos INT DECLARE #id INT DECLARE #string VARCHAR(MAX) DECLARE #default INT DECLARE #substring VARCHAR(MAX) DECLARE tempCursor CURSOR FOR SELECT id, string FROM table_name OPEN tempCursor; FETCH NEXT FROM tempCursor INTO #id, #string WHILE ##FETCH_STATUS = 0 BEGIN SET #default = 1 SET #pos = CHARINDEX('|', #string) WHILE (#pos <> 0) BEGIN SET #substring = SUBSTRING(#string, 1, #pos - 1) INSERT INTO table_name2(typeid, type, default) VALUES (#id, #substring, #default) SET #string = substring(#string, #pos+1, LEN(#string)) SET #pos = charindex('|', #string) SET #default = 0 END FETCH NEXT FROM tempCursor INTO #id, #string END CLOSE EWSCursor; DEALLOCATE EWSCursor; Hope this helps.