Split unlimited length SQL String into two columns - sql

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

Related

select one column, split to multiple columns

I have a table in Oracle Database like below:
res_code column1 ...
-------------------------------
001 A
002 B
003 C
004 D
005 E
I want to select from this table to get the result like:
res_code1 res_code2
-------------------------------
001 002
003 004
005 ...
tip: res_code1 and res_code2 both belongs to res_code, can't be duplicated.
Is is possible using sql?
You can do this simply with an SQL like:
with odds as
(
select res_code, cast(res_code as int) as rn
from myTable
where mod(cast(res_code as int) ,2) =1
),
evens as
(
select res_code, cast(res_code as int) as rn
from myTable
where mod(cast(res_code as int) ,2) =0
)
select odds.res_code as res_code1, evens.res_code as res_code2
from odds
left join evens on evens.rn = odds.rn+1;
you can try this, use row_number to make rn then do MOD to split the group.
Doing some arithmetic in main query.
CREATE TABLE T(
res_code VARCHAR(50),
column1 VARCHAR(50)
);
INSERT INTO T VALUES ('001' ,'A');
INSERT INTO T VALUES ('002' ,'B');
INSERT INTO T VALUES ('003' ,'C');
INSERT INTO T VALUES ('004' ,'D');
INSERT INTO T VALUES ('005' ,'E');
Query 1:
with cte as (
select t1.*,
MOD(row_number() over (order by column1) ,2) rn,
ceil(row_number() over (order by column1) / 2) grp1
from T t1
)
select MAX(CASE WHEN rn = 1 THEN t1.RES_CODE END) res_code1,
MAX(CASE WHEN rn = 0 THEN t1.RES_CODE END) res_code2
from cte t1
group by grp1
Results:
| RES_CODE1 | RES_CODE2 |
|-----------|-----------|
| 001 | 002 |
| 003 | 004 |
| 005 | (null) |
Use cursor to do double fetch and single insert:
declare #res_code1 nvarchar(100), #res_code2 nvarchar(100), #even int
declare #newtable table(res_code1 nvarchar(100), res_code2 nvarchar(100))
declare cur cursor for (select res_code from #mytable)
open cur
-- assume first 2 rows are available
fetch next from cur into #res_code1
fetch next from cur into #res_code2
-- set #even to 1 meaning even value has been fetched
set #even = 1
while ##FETCH_STATUS = 0
begin
if #even = 0
begin
-- fetch #res_code2
fetch next from cur into #res_code2
set #even = 1
end
else
begin
-- insert into table since #even = 1 (has even number)
insert into #newtable values(#res_code1,#res_code2)
-- fetch #res_code1
fetch next from cur into #res_code1
set #even = 0
end
end
-- insert the last odd
if #even = 1
insert into #newtable values(#res_code1,'')

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

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

SQL Transpose table - sqlserver [duplicate]

This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 8 years ago.
i have the following table
create table mytab (
mID int primary key,
pname varchar(100) not null,
pvalue varchar(100) not null
)
example data looks like
mID |pname |pvalue
-----------------------
1 |AAR | 2.3
1 |AAM | 1.2
1 |GXX | 5
2 |AAR | 5.4
2 |AAM | 3.0
3 |AAR | 0.2
I want to flip the table so that i get
mID | AAR | AAM | GXX|
---------------------------------
1 | 2.3 | 1.2 | 5|
2 | 5.4 | 3.0 | 0|
3 | 0.2 | 0 | 0
Is this somehow possible and if so, is there a way to create a dynamic query because there are lots of these pname pvalue pairs
Write Dynamic Pivot Query as:
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
DECLARE #colsFinal AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(pname)
FROM mytab
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
select #colsFinal = STUFF((SELECT distinct ',' +
'ISNULL('+QUOTENAME(pname)+',0) AS '+ QUOTENAME(pname)
FROM mytab
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
--Edited query to replace null with 0 in Final result set.
SELECT #query = 'SELECT mID, '+#colsFinal +'
FROM mytab
PIVOT
(
MAX(pvalue)
FOR pname IN(' + #cols + ')) AS p;'
exec sp_executesql #query
Check demo here..
Use pivot as following format:
select *
From (select *
From mytab)p
PIVOT(SUM(pvalue) FOR pname IN ([AAR],[AAM],[GXX]))pvt
In order to dynamic PIVOT use flowing reference:
Dynamic PIVOT Sample1
Dynamic PIVOT Sample2
Declare #t table (mID INT, pname VARCHAR(10), pvalue FLOAT)
INSERT INTO #t (mID,pname,pvalue)values (1,'AAR',2.3)
INSERT INTO #t (mID,pname,pvalue)values (1,'AAM', 1.2)
INSERT INTO #t (mID,pname,pvalue)values (1,'GXX', 5)
INSERT INTO #t (mID,pname,pvalue)values (2,'AAR', 5.4)
INSERT INTO #t (mID,pname,pvalue)values (2,'AAM', 0.3)
INSERT INTO #t (mID,pname,pvalue)values (3,'AAR', 0.2)
select mid,
CASE WHEN [AAR]IS NOT NULL THEN [AAR] ELSE ISNULL([AAR],0)END [AAR],
CASE WHEN [AAM]IS NOT NULL THEN [AAM] ELSE ISNULL([AAM],0)END [AAM],
CASE WHEN [GXX]IS NOT NULL THEN [GXX] ELSE ISNULL([GXX],0)END [GXX]
from
(
select mID, pvalue,pname
from #t
) d
pivot
(
max(pvalue)
for pname in ( [AAR], [AAM], [GXX])
) piv;

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

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