How to get words from field in SQL Server 2008 - sql

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

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 pivot a table this way when there are 3000 columns

I am using SQL Server
I have a table TT that looks like this
TargetID RowID Actual
0001 1 0
0001 2 1
0001 3 1
0002 1 0
0002 2 1
0002 3 0
0003 1 1
0003 2 1
0003 3 0
How can I pivot it is to this
RowID Target0001 Target0002 Target0003
1 0 0 1
2 1 1 1
3 1 0 0
I tried
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0001'
UNION ALL
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0002'
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0003'
But there are 3000 TargetIDs and my method is not good for that
Any idea how to do that?
You can try this...
DECLARE #ColumnsTable TABLE ([ColumnName] VARCHAR(50));
INSERT INTO #ColumnsTable ([ColumnName])
SELECT DISTINCT '[' + CONVERT(VARCHAR(48), [TargetID]) + ']'
FROM TT;
DECLARE #PivotColumns VARCHAR(MAX), #TotalColumn VARCHAR(MAX), #SQL VARCHAR(MAX);
SET #PivotColumns = (SELECT STUFF((SELECT DISTINCT ', ' + CONVERT(VARCHAR(50), [ColumnName])
FROM #ColumnsTable
FOR XML PATH('')), 1, 2, ''));
SET #SQL = 'SELECT RowID,' +#PivotColumns +'
FROM (
SELECT RowID,TargetID,Actual
FROM TT) AS t
PIVOT (MAX([Actual])
FOR [TargetID] IN (' + #PivotColumns + ')) AS p';
EXEC(#SQL);

how to set custom template for one columns in sql server? [duplicate]

I have a column with values like this:
## codel ##
1-829549-305-117
1-1196585-305-119
119.305.1983984.1 // this record
1-224594-305-121
1-1999987-305-121
122.306.113416.1 // this record
1-158059-305-122
1-1083888-305-126
Code for convert 119.305.1983984.1 to 1.1983984.305.119 is :
DECLARE #myvar varchar(20);
SET #myvar = '119.305.1983984.1';
SELECT
CONCAT(PARSENAME(#myvar, 1), '-',
PARSENAME(#myvar, 2), '-',
PARSENAME(#myvar, 3), '-',
PARSENAME(#myvar, 4))
The output should be:
## codel ##
1-829549-305-117
1-1196585-305-119
1-1983984-305-119 // this record has changed
1-224594-305-121
1-1999987-305-121
1-113416-306-122 // this record has changed
1-158059-305-122
Just replace the - with ., because PARSENAME() works only with '.', not '-'
WITH TBL AS
(
SELECT '1-829549-305-117' Str
UNION SELECT '1-1196585-305-119'
UNION SELECT '119.305.1983984.1'
UNION SELECT '1-224594-305-121'
UNION SELECT '1-1999987-305-121'
UNION SELECT '122.306.113416.1'
UNION SELECT '1-158059-305-122'
UNION SELECT '1-1083888-305-126'
),
CTE AS
(
SELECT Str,
CASE WHEN CHARINDEX('.', Str) > 0 THEN
Str
ELSE REPLACE(Str, '-', '.')
END Str1
,
CASE WHEN CHARINDEX('.', Str) > 0 THEN '.' ELSE '-' END Sep
FROM TBL
)
SELECT Str,
CONCAT(PARSENAME(Str1,1), Sep,
PARSENAME(Str1,2), Sep,
PARSENAME(Str1,3), Sep,
PARSENAME(Str1,4)
) Result
FROM CTE;
Returns:
+-------------------+-------------------+
| Str | Result |
+-------------------+-------------------+
| 1-1083888-305-126 | 126-305-1083888-1 |
| 1-1196585-305-119 | 119-305-1196585-1 |
| 1-158059-305-122 | 122-305-158059-1 |
| 119.305.1983984.1 | 1.1983984.305.119 |
| 1-1999987-305-121 | 121-305-1999987-1 |
| 122.306.113416.1 | 1.113416.306.122 |
| 1-224594-305-121 | 121-305-224594-1 |
| 1-829549-305-117 | 117-305-829549-1 |
+-------------------+-------------------+
Live Demo
If you have SQL Server 2017, then you can use CONCAT_WS() as
SELECT Str,
CONCAT_WS(Sep,
PARSENAME(Str1,1),
PARSENAME(Str1,2),
PARSENAME(Str1,3),
PARSENAME(Str1,4)
) Result
FROM CTE;
If you want the separator to be always '-'then no need to Sep, just directly '-'
You just need to add this code
SELECT [Codel],
case WHEN (CHARINDEX('.',[Codel]) > 0) then
CONCAT(PARSENAME([Codel], 1), '-',
PARSENAME([Codel], 2), '-',
PARSENAME([Codel], 3), '-',
PARSENAME([Codel], 4))
else [Codel] end
true_template
FROM [TestDB].[dbo].[CodelTbl]
Here is how you can split your string based on "." and use the values separately:
create table #temp (rn int identity(1,1), num int)
Declare #products varchar(200) = '119.305.1983984.1'
Declare #individual varchar(20) = null
WHILE LEN(#products) > 0
BEGIN
IF PATINDEX('%.%', #products) > 0
BEGIN
SET #individual = SUBSTRING(#products,
0,
PATINDEX('%.%', #products))
insert into #temp
SELECT #individual
SET #products = SUBSTRING(#products,
LEN(#individual + '.') + 1,
LEN(#products))
END
ELSE
BEGIN
SET #individual = #products
SET #products = NULL
insert into #temp
SELECT #individual
END
END
select * from #temp
Ref: How do I split a string so I can access item x?

Comma separated values from multi columns as rows

I have a table tbl_commaseperate with columns ID, MONNAME, IP and POLICY whose values are:
ID | MONNAME | IP | POLICY
-------------------------------
X | NOV | 1,2,3 | 4,5,6,7
where IP and POLICY have comma separated values.
My desired result looks like as below:
ID | MONNAME | IP | POLICY
------------------------------
X | NOV | 1 | 4
X | NOV | 2 | 5
X | NOV | 3 | 6
X | NOV | null | 7
The output is in no particular order. Also, in your desired output you don't seem to care which pair was first, which second etc. (but that can be preserved in the query, if needed).
I added a row for more testing; I have NULL for policy - which is how I realized I needed the coalesce() around the regexp_count.
with
inputs ( id ,monname, ip , policy ) as (
select 'X', 'NOV', '1,2,3' , '4,5,6,7' from dual union all
select 'X', 'DEC', '6,3,8', null from dual
)
-- end of test data; solution (SQL query) begins below this line
select id, monname,
regexp_substr(ip , '[^,]+', 1, level) as ip,
regexp_substr(policy, '[^,]+', 1, level) as policy
from inputs
connect by level <= 1 + greatest( coalesce(regexp_count(ip , ','), 0),
coalesce(regexp_count(policy, ','), 0) )
and prior id = id
and prior monname = monname
and prior sys_guid() is not null
;
ID MONNAME IP POLICY
-- ------- ----- -------
X DEC 6
X DEC 3
X DEC 8
X NOV 1 4
X NOV 2 5
X NOV 3 6
X NOV 7
7 rows selected
Please try this one.
Create a function to split comma separated string.
CREATE FUNCTION [dbo].[fnSplit](
#sInputList VARCHAR(max) -- List of delimited items
, #sDelimiter VARCHAR(max) = ',' -- delimiter that separates items
) RETURNS #List TABLE (SplitValue VARCHAR(max))
BEGIN
DECLARE #sItem VARCHAR(max)
WHILE CHARINDEX(#sDelimiter,#sInputList,0) <> 0
BEGIN
SELECT
#sItem=RTRIM(LTRIM(SUBSTRING(#sInputList,1,CHARINDEX(#sDelimiter,#sInputList,0)-1))),
#sInputList=RTRIM(LTRIM(SUBSTRING(#sInputList,CHARINDEX(#sDelimiter,#sInputList,0)+LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem
END
IF LEN(#sInputList) > 0
INSERT INTO #List SELECT #sInputList -- Put the last item in
RETURN
END
And then write your query like this.
select * from (
select SplitValue,ROW_NUMBER() over(order by SplitValue) rowNo FROM dbo.fnSplit('1,2,3',',')
) as a
full join (
select SplitValue,ROW_NUMBER() over(order by SplitValue) rowNo FROM dbo.fnSplit('4,5,6,7',',')
) as b on a.rowNo=b.rowNo
replace your column in hardcore string and. Note: You can also write with query instead of function.
Oracle
with r (id,monname,ip,policy,n,max_tokens)
as
(
select id,monname,ip,policy
,1
,greatest (nvl(regexp_count(ip,'[^,]+'),0),nvl(regexp_count(policy,'[^,]+'),0))
from tbl_commaseperate
union all
select id,monname,ip,policy
,n+1
,max_tokens
from r
where n < max_tokens
)
select r.id
,r.monname
,regexp_substr (ip ,'[^,]+',1,n) as ip
,regexp_substr (policy,'[^,]+',1,n) as policy
from r
;
CREATE TABLE #Table ( ID VARCHAR(100),MONNAME VARCHAR(100), IP VARCHAR(100) , POLICY VARCHAR(100))
INSERT INTO #Table (ID ,MONNAME, IP, POLICY)SELECT 'X','NOV',1,2,3,4','4,5,6,7'
;WITH _CTE ( _id ,_MONNAME , _IP , _POLICY , _RemIP , _RemPOLICY) AS (
SELECT ID ,MONNAME , SUBSTRING(IP,0,CHARINDEX(',',IP)), SUBSTRING(POLICY,0,CHARINDEX(',',POLICY)),
SUBSTRING(IP,CHARINDEX(',',IP)+1,LEN(IP)),
SUBSTRING(POLICY,CHARINDEX(',',POLICY)+1,LEN(POLICY))
FROM #Table
UNION ALL
SELECT _id ,_MONNAME , CASE WHEN CHARINDEX(',',_RemIP) = 0 THEN _RemIP ELSE
SUBSTRING(_RemIP,0,CHARINDEX(',',_RemIP)) END, CASE WHEN CHARINDEX(',',_RemPOLICY) = 0 THEN _RemPOLICY ELSE SUBSTRING(_RemPOLICY,0,CHARINDEX(',',_RemPOLICY)) END,
CASE WHEN CHARINDEX(',',_RemIP) = 0 THEN '' ELSE SUBSTRING(_RemIP,CHARINDEX(',',_RemIP)+1,LEN(_RemIP)) END,
CASE WHEN CHARINDEX(',',_RemPOLICY) = 0 THEN '' ELSE SUBSTRING(_RemPOLICY,CHARINDEX(',',_RemPOLICY)+1,LEN(_RemPOLICY)) END
FROM _CTE WHERE _RemIP <> '' OR _RemPOLICY <> ''
)
SELECT _id id,_MONNAME MONNAME, _IP IP , _POLICY POLICY
FROM _CTE

Transforming Table into different Table

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