SQL query to get the multiple "," positions from a string - sql

I have 5 rows of data like as below
Now I need to find the position of every ',' from my input string.
My output should be like this:

Please try this one it will give output as yours.
Create table #Table (rowNo int identity(1,1), ID varchar(100))
insert into #Table values('32132')
insert into #Table values('32132,32132')
insert into #Table values('32132,32132,6456,654,645')
declare #TableRow int = (select count(*) from #Table),#Tableloop int = 1
while(#Tableloop <= #TableRow)
begin
Declare #var varchar(100) ;
SET #var = (select ID from #Table where rowNo=#Tableloop)
declare #count int = (select len(#var) - len(replace(#var, ',', '')))
declare #loop int = 1;
declare #location int = 0;
print 'Row' + cast(#Tableloop as varchar(5))
while (#loop <= #count)
begin
SET #location = (select charindex(',',#var,#location))
print cast(#loop as varchar(5)) + ' Comma at ' + cast(#location as varchar(5))
SET #location = #location +1
SET #loop = #loop + 1;
end
SET #Tableloop = #Tableloop + 1;
END
drop table #Table
This will show proper output just put it in temp table and display it.

It looks like you are trying to split your ID values out of comma separated lists. If this is the case you would be better served creating a table-valued function that splits your comma separated list into rows.
You can use Jeff Moden's function below to achieve this using the following:
select i.ID
,ItemNumber
,Item as IDSplit
from Input i
cross apply dbo.DelimitedSplit8K(i.ID,',') s;
Which will return the following:
ID | ItemNumber | IDSplit
````````````````````````````````````````````|````````````|`````````
21321,32154,84655,65465,65476,89799,65464 | 1 | 21321
21321,32154,84655,65465,65476,89799,65464 | 2 | 32154
21321,32154,84655,65465,65476,89799,65464 | 3 | 84655
21321,32154,84655,65465,65476,89799,65464 | 4 | 65465
21321,32154,84655,65465,65476,89799,65464 | 5 | 65476
21321,32154,84655,65465,65476,89799,65464 | 6 | 89799
21321,32154,84655,65465,65476,89799,65464 | 7 | 65464
21313,32156,31656,32132 | 1 | 21313
21313,32156,31656,32132 | 2 | 32156
21313,32156,31656,32132 | 3 | 31656
21313,32156,31656,32132 | 4 | 32132
Jeff Moden's String Split function:
/****** Object: UserDefinedFunction [dbo].[DelimitedSplit8K] Script Date: 24/11/2016 12:08:35 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== String Split function by Jeff Moden: http://www.sqlservercentral.com/articles/Tally+Table/72993/
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l

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 |
+----+-------+-------+

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 |

Split a single row into multiple row [duplicate]

This question already has answers here:
How do I split a delimited string so I can access individual items?
(46 answers)
Closed 8 years ago.
How to split a single row into multiple row ?
DECLARE #var VARCHAR(50)
SELECT #Var = 'brook|456|US'
SELECT SPLIT(#var)
Result:
brook
456
US
Using a tally table:
declare #parameter varchar(4000)
set #parameter = 'brook|456|US'
set #parameter = '|' + #parameter + '|' -- add delimiter
;with
e1 as(select 1 as N union all select 1), -- 2 rows
e2 as(select 1 as N from e1 as a, e1 as b), -- 4 rows
e3 as(select 1 as N from e2 as a, e2 as b), -- 16 rows
e4 as(select 1 as N from e3 as a, e3 as b), -- 256 rows
tally as (select row_number() over(order by N) as N from e4
)
select
substring(#parameter, N+1, charindex('|', #parameter, N+1) - N-1)
from tally
where
N < len(#parameter)
and substring(#parameter, N, 1) ='|'
Try this. You need to create a Table Valed Function not a Scalar Function
CREATE FUNCTION dbo.Split (#ip_string VARCHAR(5000),#delimiter char(1))
returns TABLE
AS
RETURN
(SELECT Split.a.value('.', 'VARCHAR(100)') Split_col
FROM (SELECT Cast ('<M>' + Replace(#ip_string, #delimiter, '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a))
go
DECLARE #var VARCHAR(50)
SELECT #Var = 'brook|456|US'
SELECT * FROM dbo.Split(#var,'|')
Output :
+===========+
| Split_col |
+===========+
| brook |
+-----------+
| 456 |
+-----------+
| US |
+-----------+

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