Transforming Table into different Table - sql

I have a table like this:
RowID | ProductDescription1
-----------------------------------------------------
1 | 0296620300-0296620399;
2 | 0296620400-0296620499;0296620500-0296620599;
3 | 0296620600-0296620699;0296620700-0296620799;
I want to become like this:
NewRowID | Start | End | SourceRowID
--------------------------------------------------
1 | 0296620300 | 0296620399 | 1
2 | 0296620400 | 0296620499 | 2
3 | 0296620500 | 0296620599 | 2
4 | 0296620600 | 0296620699 | 3
5 | 0296620700 | 0296620799 | 3
Now I have a function that can do splitting stuff which returning table :
ALTER FUNCTION [dbo].[ufn_stg_SplitString]
(
-- Add the parameters for the function here
#myString varchar(500),
#deliminator varchar(10)
)
RETURNS
#ReturnTable TABLE
(
-- Add the column definitions for the TABLE variable here
[id] [int] IDENTITY(1,1) NOT NULL,
[part] [varchar](50) NULL
)
AS
BEGIN
Declare #iSpaces int
Declare #part varchar(50)
--initialize spaces
Select #iSpaces = charindex(#deliminator,#myString,0)
While #iSpaces > 0
Begin
Select #part = substring(#myString,0,charindex(#deliminator,#myString,0))
Insert Into #ReturnTable(part)
Select #part
Select #myString = substring(#mystring,charindex(#deliminator,#myString,0)+ len(#deliminator),len(#myString) - charindex(' ',#myString,0))
Select #iSpaces = charindex(#deliminator,#myString,0)
end
If len(#myString) > 0
Insert Into #ReturnTable
Select #myString
RETURN
END
I want to avoid using cursor if it's possible.
I am appreciated your comment/input.

First, this solution requires SQL Server 2005+. Second, at the bottom, I offer an alternate Split function which does not use a cursor. Third, here is a solution that does not rely on the values being of a specified length but instead that the delimiter is consistent:
Select Row_Number() Over ( Order By Z.PairNum ) As ItemNum
, Min(Case When Z.PositionNum = 1 Then Z.Value End) As [Start]
, Min(Case When Z.PositionNum = 2 Then Z.Value End) As [End]
, Z.RowId As SourceRowId
From (
Select T2.RowId, S.Value, T2.PairNum
, Row_Number() Over ( Partition By T2.RowId, T2.PairNum Order By S.Value ) As PositionNum
From (
Select T.RowId, S.Value
, Row_Number() Over ( Order By S.Value ) As PairNum
From MyTable As T
Cross Apply dbo.Split( T.ProductDescription, ';' ) As S
) As T2
Cross Apply dbo.Split( T2.Value, '-' ) As S
) As Z
Group By Z.RowId, Z.PairNum
And the Split function:
Create FUNCTION [dbo].[Split]
(
#DelimitedList nvarchar(max)
, #Delimiter nvarchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, Len(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP (Len(#DelimitedList)) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.objects As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)

SQL 2005/2008
with prods as
(
select 1 as RowID, '0296620300-0296620399;' AS ProductDescription1 union all
select 2 as RowID, '0296620400-0296620499;0296620500-0296620599;' AS ProductDescription1 union all
select 3 as RowID, '0296620600-0296620699;0296620700-0296620799;' AS ProductDescription1
)
select
ROW_NUMBER() OVER(ORDER BY RowId) as NewRowID,
LEFT(Part,10) AS Start, /*Might need charindex if they are not always 10 characters*/
RIGHT(Part,10) AS [End],
RowId as SourceRowID from prods
cross apply [dbo].[ufn_stg_SplitString] (ProductDescription1,';') p
Gives
NewRowID Start End SourceRowID
-------------------- ---------- ---------- -----------
1 0296620300 0296620399 1
2 0296620400 0296620499 2
3 0296620500 0296620599 2
4 0296620600 0296620699 3
5 0296620700 0296620799 3

Related

Split unlimited length SQL String into two columns

I have seen multiple answers, but none that worked for me.
I send in a string like this desc1$100$desc2$200 to a stored procedure.
Then I want to to insert it into a temp table like so:
|descr|meter|
|desc1|100 |
|desc2|200 |
Wanted output ^
declare #string varchar(max)
set #string = 'desc1$100$desc2$200'
declare #table table
(descr varchar(max),
meter int
)
-- Insert statement
-- INSERT NEEDED HERE
-- Test Select
SELECT * FROM #table
How should I split it?
Here's an example using JSON.
Declare #S varchar(max) = 'desc1$100$desc2$200'
Select Descr = max(case when ColNr=0 then Value end )
,Meter = max(case when ColNr=1 then Value end )
From (
Select RowNr = [Key] / 2
,ColNr = [Key] % 2
,Value
From OpenJSON( '["'+replace(string_escape(#S,'json'),'$','","')+'"]' )
) A
Group By RowNr
Results
Descr Meter
desc1 100
desc2 200
If it helps with the visualization, the subquery generates the following:
RowNr ColNr Value
0 0 desc1
0 1 100
1 0 desc2
1 1 200
Please try the following solution based on XML and XQuery.
It allows to get odd vs. even tokens in the input string.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, descr varchar(max), meter int);
DECLARE #string varchar(max) = 'desc1$100$desc2$200';
DECLARE #separator CHAR(1) = '$';
DECLARE #xmldata XML = TRY_CAST('<root><r><![CDATA[' +
REPLACE(#string, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
INSERT INTO #tbl (descr, meter)
SELECT c.value('(./text())[1]', 'VARCHAR(30)') AS descr
, c.value('(/root/*[sql:column("seq.pos")]/text())[1]', 'INT') AS meter
FROM #xmldata.nodes('/root/*[position() mod 2 = 1]') AS t(c)
CROSS APPLY (SELECT t.c.value('let $n := . return count(/root/*[. << $n[1]]) + 2','INT') AS pos
) AS seq;
-- Test
SELECT * FROM #tbl;
Output
+----+-------+-------+
| ID | descr | meter |
+----+-------+-------+
| 1 | desc1 | 100 |
| 2 | desc2 | 200 |
+----+-------+-------+

How to transform rows into column in SQL Server?

Using MS SQL Server and I have below table:
+-----------+------------------+------------------+---------------------+-------------------------+
| SrNo | ApprCode | ApprName | ApprStatus | ApprDate |
+-----------+------------------+------------------+---------------------+-------------------------+
| SR_176 | X001 | James | APR | 2019-10-03 |
| SR_176 | X002 | Sam | APR | 2019-10-03 |
+-----------+------------------+------------------+---------------------+-------------------------+
Tried with PIVOT but its showing james and X001 as a column heading:
Expected Result:
+-----------+-------------- +---------------+---------------------+-------------------------+------------------+
SrNo | ApprCode_1 | ApprName_1 | ApprDate_2 ApprCode_2 ApprName_2 ApprDate_2
+-----------+---------------+---------------+---------------------+-------------------------+------------------+
SR_176 X001 James 2019-10-03 X002 Sam 2019-10-03
+-----------+---------------+---------------+---------------------+---- ---------------------+------------------+
Query to generate data:
CREATE TABLE #Temp
(
SrNo NVARCHAR(200),
ApprCode NVARCHAR(200),
ApprName NVARCHAR(200),
ApprDate Date
)
INSERT INTO #Temp VALUES ('SR_176','X001','James', '2019-10-03')
INSERT INTO #Temp VALUES ('SR_176','X002','Sam', '2019-10-03')
Query that I tried:
declare #sql nvarchar(max)
declare #name_concat nvarchar(max)
declare #name1_concat nvarchar(max)
declare #select_aggs nvarchar(max)
select #name_concat = STUFF((select distinct ',' + quotename(ApprCode) from #Temp order by 1 for xml path('')), 1, 1, '')
select #name1_concat = STUFF((select distinct ',' + quotename(ApprName) from #Temp order by 1 for xml path('')), 1, 1, '')
select #sql = '
;with cte2 as
(
SELECT SrNo,' + #name_concat + ',' + #name1_concat + '
FROM #Temp
PIVOT(MAX(ApprCode)
FOR ApprCode IN (' + #name_concat + ')) AS PVTTable PIVOT
(
MAX(ApprName)
FOR ApprName IN (' + #name1_concat + ')) AS PVTTable1
)
select * from cte2
'
exec sp_executesql #sql
In fact, This is not a SQL Pivot situation and the sample data for question is not enough to test it completely but you can find the main idea :
Select
SrNo,
MAX(IIF(ApprCode = 'X001', ApprCode, null)) as ApprCode_1,
MAX(IIF(ApprCode = 'X001', ApprName, null)) as ApprName_1,
MAX(IIF(ApprCode = 'X001', ApprDate, null)) as ApprDate_1,
MAX(IIF(ApprCode = 'X002', ApprCode, null)) as ApprCode_2,
MAX(IIF(ApprCode = 'X002', ApprName, null)) as ApprName_2,
MAX(IIF(ApprCode = 'X002', ApprDate, null)) as ApprDate_2
From #Temp
Group by SrNo
This code works if ApprCode was the key to make two separate columns & SrNo is for the group by between rows.
I solved it by using ROW_NUMBER() and Case Expression:
Here is the query:
SELECT *,
Row_number()
OVER(
partition BY srno
ORDER BY apprdate) AS RN
INTO #temptable
FROM #temp
SELECT srno,
CASE rn
WHEN 1 THEN Max(apprname)
END AS [1 Approver],
CASE rn
WHEN 2 THEN Max(apprname)
END AS [2 Approver],
CASE rn
WHEN 1 THEN Max(apprcode)
END AS [1 ApproverCode],
CASE rn
WHEN 2 THEN Max(apprcode)
END AS [2 ApproverCode],
CASE rn
WHEN 1 THEN Max(apprdate)
END AS [1 Date],
CASE rn
WHEN 2 THEN Max(apprdate)
END AS [2 Date]
INTO #james
FROM #temptable
GROUP BY srno,
rn
SELECT srno,
Max([1 approver]) AS ApproverName_1,
Max([1 approvercode]) AS ApproverCode_1,
Max([1 date]) AS ApproverDate_1,
Max([2 approver]) AS ApproverName_2,
Max([2 approvercode]) AS ApproverCode_2,
Max([2 date]) AS ApproverDate_2
FROM #james
GROUP BY srno

Split string into multiple rows with multiple columns in paired

I have a table as below:
Order No | Customers | Amount
---------+----------------------+-------------
1 | Briant~~Luck | 23~~2122
2 | Mike~~Lee~~David | 10~~200~37
3 | Stak | 100
With format, each customer has one value in Amount.
I'm trying to figure out how to expand the ~~ delimited values to populate a new customers table, which should look like this:
Order No | Customer | Amount
---------+----------------------+---------
1 | Briant | 23
1 | Luck | 2122
2 | Mike | 10
2 | Lee | 200
2 | David | 37
3 | Stak | 100
How can I do?
Any solution in SQL query, function or cursor is appreciated.
Thanks
I think you could store data as your expected result structure. It is much better.
Btw you could use a split function to get your output
DECLARE #SampleData AS TABLE
(
OrderNo int,
Customers varchar(200),
Amount varchar(200)
)
INSERT INTO #SampleData
(
OrderNo,
Customers,
Amount
)
VALUES
( 1, 'Briant~~Luck','23~~2122'),
( 2, 'Mike~~Lee~~David','10~~200~~37'),
( 3, 'Stak','100')
SELECT sd.OrderNo, c.[Value] AS Customer, a.[Value] AS Amount
FROM #SampleData sd
CROSS APPLY
(
SELECT Pos, Value
FROM [dbo].[SplitString](sd.Customers,'~~')
) c
CROSS APPLY
(
SELECT Pos, Value
FROM [dbo].[SplitString](sd.Amount,'~~')
) a
WHERE c.Pos = a.Pos
ORDER BY sd.OrderNo
Split function
CREATE FUNCTION [dbo].[SplitString] (#Text varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select Pos = Row_Number() over (Order By (Select null))
,Value = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ Replace(#Text,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
Demo link: http://rextester.com/XRX32958
This solution uses XML, CROSS APPLY & ROW_NUMBER to deconstruct the '~~' seperated fields.
It doesn't require a UDF or the STRING_SPLIT function from SQL Server 2016.
-- Using a table variable for the test
declare #Orders table ([Order No] int, Customers varchar(30), Amount varchar(30));
insert into #Orders ([Order No], Customers, Amount) values
(1,'Briant~~Luck','23~~2122'),
(2,'Mike~~Lee~~David','10~~200~~37'),
(3,'Stak','100');
SELECT C.[Order No], C.Customer, A.Amount
FROM
(
SELECT
[Order No],
row_number() over (partition by [Order No] order by (select 1)) as rn,
Customers.Name.value('.', 'VARCHAR(100)') AS Customer
FROM (
SELECT [Order No], CAST ('<x>' + REPLACE(Customers, '~~', '</x><x>') + '</x>' AS XML) AS XCustomers
FROM #Orders
) AS Q1
CROSS APPLY Q1.XCustomers.nodes ('/x') AS Customers(Name)
) C
JOIN (
SELECT
[Order No],
row_number() over (partition by [Order No] order by (select 1)) as rn,
Amounts.Value.value('.', 'VARCHAR(100)') AS Amount
FROM (
SELECT [Order No], CAST ('<x>' + REPLACE(Amount, '~~', '</x><x>') + '</x>' AS XML) AS XAmounts
FROM #Orders
) AS Q1
CROSS APPLY Q1.XAmounts.nodes ('/x') AS Amounts(Value)
) A
ON (C.[Order No] = A.[Order No] AND C.RN = A.RN);
Or if you know there will be maximum 3 values in those strings.
Then the trick with PARSENAME could be used:
SELECT [Order No],
PARSENAME(REPLACE(Customers, '~~', '.'), v.n) as Customer,
PARSENAME(REPLACE(Amount, '~~', '.'), v.n) as Amount
FROM #Orders t
JOIN (VALUES (1),(2),(3)) AS v(n)
ON v.n <= (len(Customers) - len(replace(Customers, '~~', ','))+1);
If you are on SQL2016+ then try this:
drop table if exists dbo.Orders;
create table dbo.Orders (
ID int
, Customers varchar(100)
, Amount varchar(100)
);
insert into dbo.Orders (ID, Customers, Amount)
values (1,'Briant~~Luck', '23~~2122')
, (2, 'Mike~~Lee~~David', '10~~200~~37')
, (3, 'Stak', '100');
select
o.ID
, t01.value as Customer
, t02.value as Amount
from dbo.Orders o
outer apply (
select
ROW_NUMBER () over (order by o.Customers ASC) as Rbr
, t.value
from string_split (replace(o.Customers, '~~', '~'), '~') t
) t01
outer apply (
select
ROW_NUMBER () over (order by o.Amount ASC) as Rbr
, t.value
from string_split (replace(o.Amount, '~~', '~'), '~') t
) t02
where t01.Rbr = t02.Rbr
If you are on a version of SQL Server prior to 2016, you will need to create your own String Splitting function and reference that in your script. The version I use is as follows:
create function [dbo].[StringSplit]
(
#str nvarchar(4000) = ' ' -- String to split.
,#delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,#num as int = null -- Which value to return.
)
returns table
as
return
( -- Start tally table with 10 rows.
with n(n) as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n))
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(#str) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(#str,t,1) = #delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(#delimiter,#str,s),0)-s,4000) from s)
select rn as ItemNumber
,Item
from(select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
) a
where rn = #num -- Return a specific value where specified,
or #num is null -- Or the everything where not.
)
And is used as follows. Note that I have split the outer apply into separate queries to avoid row duplication:
declare #t table(OrderNo int,Customers nvarchar(500),Amount nvarchar(500));
insert into #t values
(1,'Briant~~Luck','23~~2122')
,(2,'Mike~~Lee~~David','10~~200~~37')
,(3,'Stak','100');
with c as
(
select t.OrderNo
,c.ItemNumber
,c.Item as Customers
from #t t
outer apply dbo.StringSplit(replace(t.Customers,'~~','|'),'|',null) c
),a as
(
select t.OrderNo
,a.ItemNumber
,a.Item as Amount
from #t t
outer apply dbo.StringSplit(replace(t.Amount,'~~','|'),'|',null) a
)
select c.OrderNo
,c.Customers
,a.Amount
from c
join a
on(c.OrderNo = a.OrderNo
and c.ItemNumber = a.ItemNumber
)
order by a.OrderNo
,c.Customers;
Output:
+---------+-----------+--------+
| OrderNo | Customers | Amount |
+---------+-----------+--------+
| 1 | Briant | 23 |
| 1 | Luck | 2122 |
| 2 | David | 37 |
| 2 | Lee | 200 |
| 2 | Mike | 10 |
| 3 | Stak | 100 |
+---------+-----------+--------+
Here solution as plsql
declare
type t_array_text is table of varchar2(30);
type t_array_number is table of number;
array_text t_array_text := t_array_text();
array_number t_array_number := t_array_number();
i number := 1;
cursor c1 is
select * from deneme;
begin
FOR c in c1
LOOP
i := 1;
array_text := t_array_text();
array_number := t_array_number();
for rec in (
SELECT regexp_substr(c.customer, '[[:alpha:]]+', 1, level) a frOM dual
CONNECT BY level<=regexp_count(c.customer,'~~')+1)
loop
array_text.extend();
array_text (i) := rec.a;
i := i + 1;
end loop;
i := 1;
for rec in (
SELECT regexp_substr(c.amount, '[0-9]+', 1, level) a frOM dual
CONNECT BY level<=regexp_count(c.amount,'~~')+1)
loop
array_number.extend();
array_number (i) := rec.a;
i := i + 1;
end loop;
for y in 1..array_text.count loop
dbms_output.put_line (c.order_no || ' ' || array_text(y) || ' ' || array_number(y));
end loop;
END LOOP;
end;
result as follows:
1 Briant 23
1 Luck 2122
2 Mike 10
2 Lee 200
2 David 37
3 Stak 10

Remove 'cross' duplicate result for double cross apply

I have the following data in a column:
5;ABC|1;XYZ
I would like to split the value on the '|' delimiter and then split every result on the ';' delimiter.
I have the following query, but unfortenately it gives me (sort of) duplicate results.
DECLARE #MyTable TABLE ( Code VARCHAR(100) )
INSERT INTO #MyTable
VALUES ( '5;ABC|1;XYZ' );
WITH Query AS
(
SELECT T1.RowNum, SubSplit.Value FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY Code) as RowNum, Split.Value FROM #MyTable
CROSS APPLY SplitString(Code, '|') AS Split
) T1
CROSS APPLY SplitString(Value, ';') AS SubSplit
)
SELECT q1.Value AS [Left], q2.Value AS [Right] FROM Query q1
INNER JOIN Query q2 ON q1.RowNum = q2.RowNum AND q1.Value <> q2.Value
The result is:
But what I would like is:
How can I achieve this?
Edit
For completeness, this is the SplitString function I use:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION SplitString
(
-- Add the parameters for the function here
#input varchar(8000),
#delimiter varchar(1)
)
RETURNS TABLE
AS
RETURN
(
WITH cte AS
(
SELECT 0 a, 1 b
UNION ALL
SELECT b, CHARINDEX(#delimiter, #input, b) + LEN(#delimiter)
FROM CTE
WHERE b > a
)
SELECT SUBSTRING(#input, a,
CASE WHEN b > LEN(#delimiter)
THEN b - a - LEN(#delimiter)
ELSE LEN(#input) - a + 1 END) Value
FROM cte WHERE a > 0
)
GO
Add another ROW_NUMBER in the outer query:
SQL Fiddle
Query 1:
DECLARE #MyTable TABLE ( Code VARCHAR(100) )
INSERT INTO #MyTable
VALUES ( '5;ABC|1;XYZ|6;HXS|7;GGH' )
;WITH Query AS
(
SELECT T1.RowNum, SubSplit.Value,
ROW_NUMBER() OVER (PARTITION BY T1.RowNum ORDER BY SubSplit.Value) as RowNum1
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY Code) as RowNum, Split.Value FROM #MyTable
CROSS APPLY SplitString(Code, '|') AS Split
) T1
CROSS APPLY SplitString(Value, ';') AS SubSplit
)
SELECT q1.Value AS [Left], q2.Value AS [Right] FROM Query q1
INNER JOIN Query q2 ON q1.RowNum = q2.RowNum AND q1.RowNum1 = 1 AND q2.RowNum1 = 2
Results:
| Left | Right |
|------|-------|
| 5 | ABC |
| 1 | XYZ |
| 6 | HXS |
| 7 | GGH |
Update:
The use of the ROW_NUMBER() in the outer query will only work if the Left is less then Right when diong the string comparison. It will not work correctly for the '6;123' value. Therefore, there is a better approach by using an enhanced SplitString function as per below:
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE FUNCTION SplitString
(
-- Add the parameters for the function here
#input varchar(8000),
#delimiter varchar(1)
)
RETURNS TABLE
AS
RETURN
(
WITH cte AS
(
SELECT 0 a, 1 b, 0 rn
UNION ALL
SELECT b, CHARINDEX(#delimiter, #input, b) + LEN(#delimiter), rn + 1
FROM CTE
WHERE b > a
)
SELECT SUBSTRING(#input, a,
CASE WHEN b > LEN(#delimiter)
THEN b - a - LEN(#delimiter)
ELSE LEN(#input) - a + 1 END) Value,
rn
FROM cte WHERE a > 0
)
Query 1:
DECLARE #MyTable TABLE ( Code VARCHAR(100) )
INSERT INTO #MyTable
VALUES ( '5;ABC|1;XYZ|6;123|7;GGH' )
;WITH Query AS
(
SELECT T1.RowNum, SubSplit.Value,
SubSplit.rn as RowNum1
FROM
(
SELECT Split.rn as RowNum, Split.Value FROM #MyTable
CROSS APPLY SplitString(Code, '|') AS Split
) T1
CROSS APPLY SplitString(Value, ';') AS SubSplit
)
SELECT q1.Value AS [Left], q2.Value AS [Right] FROM Query q1
INNER JOIN Query q2 ON q1.RowNum = q2.RowNum AND q1.RowNum1 = 1 AND q2.RowNum1 = 2
Results:
| Left | Right |
|------|-------|
| 5 | ABC |
| 1 | XYZ |
| 6 | 123 |
| 7 | GGH |

How to get words from field in SQL Server 2008

I need to get the words in a text field and make some updates with those words, for example:
original data
words | other field | another field
---------------------------------------------
white | |
some words | |
some other w | |
desired result
words | other field | another field
---------------------------------------------
white | |
some | words |
some | other | w
How can I get this done?
EXTRA
I have this query where I get how many words a field have
select nombre,
LEN(words) - LEN(REPLACE(words, ' ', ''))+1 as palabras
from origen_informacion
where words <> ''
If you are trying to split a space separated string you can use this function:
create function fn_ParseCSVString
(
#CSVString varchar(8000) ,
#Delimiter varchar(10)
)
returns #tbl table (s varchar(1000))
as
/*
select * from dbo.fn_ParseCSVString ('qwe rew wer', ',c,')
*/
begin
declare #i int ,
#j int
select #i = 1
while #i <= len(#CSVString)
begin
select #j = charindex(#Delimiter, #CSVString, #i)
if #j = 0
begin
select #j = len(#CSVString) + 1
end
insert #tbl select substring(#CSVString, #i, #j - #i)
select #i = #j + len(#Delimiter)
end
return
end
GO
And pass ' ' as your delimiter.
select * from dbo.fn_ParseCSVString ('qwe rew wer', ' ')
In SQL Server 2008 you can use sys.dm_fts_parser to split a string into words. You could incorporate this with cross apply.
DECLARE #data TABLE
(
id INT IDENTITY(1,1) PRIMARY KEY,
words VARCHAR(1000),
other_field VARCHAR(1000),
another_field VARCHAR(1000)
)
INSERT INTO #data (words)
VALUES ('white'),('some words'),('some other w '),('This sentence has 5 words');
WITH splitData AS
(
SELECT
id ,
max(case when occurrence = 1 then display_term end) as word1,
max(case when occurrence = 2 then display_term end) as word2,
max(case when occurrence = 3 then display_term end) as word3,
max(case when occurrence = 4 then display_term end) as word4
FROM #data
CROSS APPLY sys.dm_fts_parser('"' + REPLACE(words,'"','') + '"',1033,NULL,0)
GROUP BY id
HAVING MAX(occurrence) <= 4
)
UPDATE #data
SET words = word1, other_field=word2, another_field=word3 + ISNULL(' ' + word4,'')
FROM #data d1
JOIN splitData sd ON d1.id = sd.id
SELECT * FROM #data
Output
id words other_field another_field
------ ------------------------------ --------------- --------------
1 white NULL NULL
2 some words NULL
3 some other w
4 This sentence has 5 words NULL NULL