Split sql string value based on 7th delimiter - sql

Present column value
(Below is column value from temp table, value here is dynamically changing)
45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc
Need to divide based on 7th PIPE i.e after Test Msg
Output should be
String1
45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg
and (as a second string)
String 2
TBL101 | PC | 1.00 | COMP101 | CS | 1.00......... etc
Function
CREATE FUNCTION dbo.SUBSTRING_INDEX
(
#str NVARCHAR(4000),
#delim NVARCHAR(1),
#count INT
)
RETURNS NVARCHAR(4000)
WITH SCHEMABINDING
BEGIN
DECLARE #XmlSourceString XML;
SET #XmlSourceString = (SELECT N'<root><row>' + REPLACE( (SELECT #str AS '*' FOR XML PATH('')) , #delim, N'</row><row>' ) + N'</row></root>');
RETURN STUFF
(
((
SELECT #delim + x.XmlCol.value(N'(text())[1]', N'NVARCHAR(4000)') AS '*'
FROM #XmlSourceString.nodes(N'(root/row)[position() <= sql:variable("#count")]') x(XmlCol)
FOR XML PATH(N''), TYPE
).value(N'.', N'NVARCHAR(4000)')),
1, 1, N''
);
END
GO
DECLARE #EmpId NVARCHAR(1000)
select #EmpId = temp from OMSOrderTemp
SELECT dbo.SUBSTRING_INDEX(#EmpId, N'|', 7) AS Result;e
Here in Result only string1 is showing and only first row.

Spend time for you and happy that come with solution , I have modify the your function with own logic you can try this, This is table value function i.e this function will return Table
CREATE FUNCTION dbo.SUBSTRING_INDEX
(
#str NVARCHAR(4000),
#delim NVARCHAR(1),
#count INT
)RETURNS #rtnTable TABLE
(
FirstString NVARCHAR(2000),
SecondString NVARCHAR(2000)
)
AS
BEGIN
DECLARE #cnt INT=1;
DECLARE #subStringPoint INT = 0
WHILE #cnt <=#count
BEGIN
SET #subStringPoint=CHARINDEX(#delim,#str,#subStringPoint)+1
SET #cnt=#cnt+1
END
INSERT INTO #rtnTable
SELECT SUBSTRING(#str,0,#subStringPoint-1) ,SUBSTRING(#str,#subStringPoint+1,LEN(#str))
RETURN
END
To call this function
DECLARE #s varchar(MAX)='45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00'
SELECT * FROM dbo.SUBSTRING_INDEX (#s,'|',7)
This will gives two column output
45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg TBL101 | PC | 1.00 | COMP101 | CS | 1.00

Finally got a solution almost identical to #JaydipJ. I thought to implement in a different way but the following should do using While loop:
DECLARE #str VARCHAR(1000),
#str1 VARCHAR(1000),
#str2 VARCHAR(1000),
#pos INT,
#counter INT
SET #str = '45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc'
SET #counter = 0
SET #pos = 0
WHILE #counter < 7
BEGIN
SET #pos = CHARINDEX('|', #str, #pos + 1) ---- Gets the position of delimiter '|'
SET #counter = #counter + 1 ---- Increments the counter on the given counter value
END
SET #str1 = SUBSTRING(#str, 1, #pos) ---- Splits the string on the 7th position of delimiter '|'
SET #str2 = SUBSTRING(#str, #pos + 1, LEN(#str) - #pos) ---- Splits the rest of the string
Print 'str1='+ #str1
Print 'str2='+ #str2
SELECT #str1 AS String1, #str2 AS String2
The While loop is used to iterate through the string and getting the Delimiter position, it splits the string.

Related

SQL Concatenate Strings in order of line numbers

I am using SQL Server 2014 Standard.
I have the following query...
SELECT ach.amt, ades.dsline, ades.des
FROM ##ACHTrans ach
LEFT OUTER JOIN apvodes ades on 1=1 and ades.vo_id = ach.vo_id
WHERE ades.voline = '100'
ORDER by ach.apnum, ach.cknum, ach.vo_id, ach.amt desc
Which gives me the results...
+------------+---------------+------------------------------+
| ach.amt | ades.dsline | ades.des |
+------------+---------------+------------------------------+
| 1232.50 | 1 | This is the description for |
| 1232.50 | 2 | The $1,232.50 ACH Amount |
| 245.18 | 1 | This one is for the $245.18 |
| 245.18 | 2 | transactions details |
| 245.18 | 3 | that has four lines of info |
| 245.18 | 4 | in the description. |
| 79.25 | 1 | This $79.25 item has 1 line. |
| 15.00 | 1 | So does this $15.00 one. |
+------------+---------------+------------------------------+
I need a way to snag this info by the ach.amt line, and concatenate the ades.des info for results similar to:
+------------+--------------------------------------------------------------------------------------------------+
| Amount | Description |
+------------+--------------------------------------------------------------------------------------------------+
| 1232.50 | This is the description for The $1,232.50 ACH Amount |
| 245.18 | This one is for the $245.18 transactions details that has four lines of info in the description. |
| 79.25 | This $79.25 item has 1 line. |
| 15.00 | So does this $15.00 one. |
+------------+--------------------------------------------------------------------------------------------------+
This is what string_agg() does:
select ach.amt,
string_agg(des, ',') within group (order by dsline)
from t
group by ach.amt;
Without STRING_AGG you would use for XML PATH like so:
DECLARE #table TABLE (amt MONEY, dsline INT, [des] VARCHAR(1000));
INSERT #table VALUES
(1232.50,1,'This is the description for'),
(1232.50,2,'The $1,232.50 ACH Amount'),
( 245.18,1,'This one is for the $245.18'),
( 245.18,2,'transactions details'),
( 245.18,3,'that has four lines of info'),
( 245.18,4,'in the description.'),
( 79.25,1,'This $79.25 item has 1 line.'),
( 15.00,1,'So does this $15.00 one.');
SELECT
amt,
[Description] =
(
SELECT t2.[des]+''
FROM #table AS t2
WHERE t.amt = t2.amt
ORDER BY t2.dsline
FOR XML PATH('')
)
-- string_agg(des, ',') within group (order by dsline)
FROM #table AS t
GROUP BY amt;
Results:
amt Description
--------------------- ---------------------------------------------------------------------------------------------
15.00 So does this $15.00 one.
79.25 This $79.25 item has 1 line.
245.18 This one is for the $245.18transactions detailsthat has four lines of infoin the description.
1232.50 This is the description forThe $1,232.50 ACH Amount
This may not be the prettiest solution but I have had to deal with something similar and used a cursor to concatenate my strings in a temporary table and then used that in my final join statement back to the original table. I used table variables so you can play with it yourself.
Following is a code example you can play with:
declare #tableAmt table (
IDNum int,
Amt Money
)
declare #tableDesc table (
IDNum int,
LineNum int,
Info varchar(10)
)
set nocount on
insert #tableAmt (IDNum, Amt)
values (1,100.00),
(2,125.00)
insert #tableDesc (IDNum, LineNum, Info)
values (1,1,'some text'),
(1,2,'more text'),
(2,1,'different'),
(2,2,'text'),
(2,3,'final')
declare #description table
(IDNum int,
ConcatDesc varchar(30)
)
declare #id int,
#oldid int,
#string char(10),
#finalstring varchar(30)
declare getdata_cursor cursor for
select IDNum, Info
from #tableDesc
order by IDNum, LineNum
open getdata_cursor
fetch next from getdata_cursor into
#id, #string
while ##FETCH_STATUS=0
begin
if #oldid <> #id
begin
insert #description(IDNum, ConcatDesc)
values(#oldid, #finalstring)
select #finalstring = ''
end
select #finalstring = isnull(#finalstring,'') + rtrim(#string) + ' '
select #string = '', #oldid = #id
fetch next from getdata_cursor into
#id, #string
end
insert #description(IDNum, ConcatDesc)
values(#oldid, #finalstring)
close getdata_cursor
deallocate getdata_cursor
select ta.IDNum, Amt, ConcatDesc from #tableAmt ta join #description d
on ta.IDNum = d.IDNum

Moving result from 1:n query to repeating columns

I would like to achieve the following from a result of a 1:n join using T-sql
Surname | Givename |..Address | City
Name1....| Givename1.|..Addr11...| City11
Name1....| Givename1.|..Addr12...| City12
Name2....| Givename2.|..Addr21...| City21
Name2....| Givename2.|..Addr22...| City22
Name2....| Givename2.|..Addr23...| City23
TO:
Surname | Givename.. | Address | City... | Address | City... | Address | City
Name1....| Givename1...| Addr11....| City11. | Addr12....| City12. |
Name2....| Givename2...| Addr21....| City21. | Addr22....| City22. | Addr23....| City23
I not care about repeating columnames. Up if there is a soultion with numbers in the repeating columns it would be fine too.
Thanks
In my opinion Pivot is not a solution in this case. Because the column name should repat, and in pivot, cell values are moved to column names, also is unlike pivot no aggregate function involved.
In my thought, the following query will handle your issue. However the SQL Server has column number limit for tables.
Columns per table 1,024 Tables that include sparse column sets
include up to 30,000 columns. See sparse column sets.
You should take this considiration for your data.
DROP TABLE IF EXISTS #Test
DROP TABLE IF EXISTS ##PivotUnlimited
CREATE TABLE #Test(Surname VARCHAR(100) , GivenName VARCHAR(200),
Adress VARCHAR(100),City VARCHAR(100))
INSERT INTO #Test
VALUES
('Name1','Givename1','Addr11','City11'),
('Name1','Givename1','Addr12','City12'),
('Name2','Givename2','Addr21','City21'),
('Name2','Givename2','Addr22','City21'),
('Name2','Givename2','Addr23','City23')
,('Name5','Givename5','Addr51','City51'),
('Name5','Givename5','Addr52','City52'),
('Name5','Givename5','Addr53','City53'),
('Name5','Givename5','Addr54','City54'),
('Name3','Givename3','Addr31','City31')
DECLARE #Counter AS INT=1
DECLARE #Max AS INT
DECLARE #SQL AS VARCHAR(MAX)
SELECT #Max= MAX(DetMax) FROM
(
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS DetMax FROM #Test
) AS TMP
DROP TABLE IF EXISTS ##PivotUnlimited
CREATE TABLE ##PivotUnlimited (Surname VARCHAR(100),GivenName VARCHAR(100))
WHILE #Counter <=#Max
BEGIN
SET #SQL= 'ALTER TABLE ##PivotUnlimited ADD ADDR' + CONVERT(VARCHAR,#Counter) + ' VARCHAR(100)'
EXEC(#SQL)
SET #SQL= 'ALTER TABLE ##PivotUnlimited ADD City' + CONVERT(VARCHAR,#Counter) + ' VARCHAR(100)'
EXEC(#SQL)
SET #Counter=#Counter+1
END
INSERT INTO ##PivotUnlimited (Surname,GivenName)
SELECT DISTINCT Surname , GivenName FROM #Test
DECLARE #Name AS VARCHAR(100)
DECLARE cursorT CURSOR
FOR
SELECT DISTINCT Surname from #test
OPEN cursorT
FETCH NEXT FROM cursorT INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #CounterCursor AS INT=1
DECLARE #MaxCursort AS INT =0
DECLARE #UpdateSQL AS VARCHAR(MAX)
DECLARE #UptAdr AS VARCHAR(100)
DECLARE #UptCity AS VARCHAR(100)
SELECT #MaxCursort=RowNumber FROM (
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS RowNumber FROM #Test
WHERE Surname=#Name
) AS TMP_TBL
WHILE #CounterCursor<= #MaxCursort
BEGIN
SELECT #UptAdr=Adress ,#UptCity=City FROM (
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS RowNumber,* FROM #Test
) AS TMP_TBL WHERE RowNumber=#CounterCursor and Surname=#Name
SET #UpdateSQL= 'UPDATE ##PivotUnlimited SET ADDR' + CONVERT(VARCHAR,#CounterCursor) + ' = ' + '''' + #UptAdr +'''' + ' , '
+ ' City' + CONVERT(VARCHAR,#CounterCursor) + ' = ' + '''' + #UptCity +'''' + ' WHERE Surname = ' + '''' + #Name + ''''
EXEC (#UpdateSQL)
SET #CounterCursor=#CounterCursor+1
END
FETCH NEXT FROM cursorT INTO #Name
END
CLOSE cursorT
DEALLOCATE cursorT
SELECT * FROM ##PivotUnlimited
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+
| Surname | GivenName | ADDR1 | City1 | ADDR2 | City2 | ADDR3 | City3 | ADDR4 | City4 |
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+
| Name1 | Givename1 | Addr11 | City11 | Addr12 | City12 | NULL | NULL | NULL | NULL |
| Name2 | Givename2 | Addr21 | City21 | Addr22 | City21 | Addr23 | City23 | NULL | NULL |
| Name3 | Givename3 | Addr31 | City31 | NULL | NULL | NULL | NULL | NULL | NULL |
| Name5 | Givename5 | Addr51 | City51 | Addr52 | City52 | Addr53 | City53 | Addr54 | City54 |
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+

SQL separate for stored procedure

About SQL Server Management Studio stored procedure.
The following variables '|' I want to separate it from. How can I do it?
'628391|28100|8304|3|1201129|12|Kg|M01|SERIOUSLY CHUNKY WOOL'
'627452|70462|618|60|100059|72|Ad|M01|THICK & QUICK STRIPES'
'617213|99233|89|10|18754|12|Kg|M01|FASHION KC ARAN 400'
You can use the following query
DECLARE #PL AS VARCHAR(MAX)='628391|28100|8304|3|1201129|12|Kg|M01|SERIOUSLY CHUNKY WOOL'
SELECT value FROM string_split(#PL,'|')
+-----------------------+
| value |
+-----------------------+
| 628391 |
| 28100 |
| 8304 |
| 3 |
| 1201129 |
| 12 |
| Kg |
| M01 |
| SERIOUSLY CHUNKY WOOL |
+-----------------------+
EDIT
DROP TABLE IF EXISTS TestStrList
DECLARE #query AS NVARCHAR(MAX)
DECLARE #PL AS VARCHAR(MAX)='628391|28100|8304|3|1201129|12|Kg|M01|SERIOUSLY CHUNKY WOOL'
DECLARE #cOL AS VARCHAR(MAX)
SELECT value INTO
TestStrList FROM string_split(#PL,'|')
select #cOL = STUFF((SELECT ',' + QUOTENAME(value)
FROM TestStrList
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cOL + N' from
(
select value
from TestStrList
) x
pivot
(
max(value)
for value in (' + #cOL + N')
) p '
exec sp_executesql #query;
+--------+-------+------+---+---------+----+----+-----+-----------------------+
| 628391 | 28100 | 8304 | 3 | 1201129 | 12 | Kg | M01 | SERIOUSLY CHUNKY WOOL |
+--------+-------+------+---+---------+----+----+-----+-----------------------+
| 628391 | 28100 | 8304 | 3 | 1201129 | 12 | Kg | M01 | SERIOUSLY CHUNKY WOOL |
+--------+-------+------+---+---------+----+----+-----+-----------------------+
I'm not sure what you mean with "seperate it" but the following SQL QUERY might be helpful for you.
DECLARE #text VARCHAR(200) = '617213|99233|89|10|18754|12|Kg|M01|FASHION KC ARAN 400'
WHILE 1 = 1
BEGIN
DECLARE #index INT = CHARINDEX('|', #text, 1)
IF #index = 0
RETURN
SELECT LEFT(#text, #index - 1)
SET #text = SUBSTRING(#text, #index + 1, LEN(#text) - #index)
END
Basically, the code with using CHARINDEX, is trying to find out the index of the first pipe (from left) in the text. After finding the index, it's applies a SUBSTRING to get exact text.

Passing dynamic select values to cursor in SQL (cursor usage)

I'm new to cursors,
How should I write my below code in cursor to pass column value to variable #str
Basically #str in below code is taking static value, need to pass table column values
DECLARE #str VARCHAR(1000),
#str1 VARCHAR(1000),
#str2 VARCHAR(1000),
#pos INT,
#counter INT
SET #str = '45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc'
(Here i need to pass table column value like--- select name from Order so every time it takes new value)
--select #str = temp FROM OrderTemp
SET #counter = 0
SET #pos = 0
WHILE #counter <= 6
BEGIN
SET #pos = CHARINDEX('|', #str, #pos + 1)
SET #counter = #counter + 1
END
SET #str1 = SUBSTRING(#str, 1, #pos)
SET #str2 = SUBSTRING(#str, #pos+1, LEN(#str)-#pos)
insert into OrderInterface (name)(SELECT #str2)
insert into OrderInterface (Id)(SELECT #str1)
Input
#str :-- '45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc'
Now i need to pass Input value from table like : Select name from tablename instead of static values.
Ouput
#str1 :-- '45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg'
#str2 :-- 'TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc'
OK, Assuming #Temp looks something like this:
Then we can do the following:
;with cte as (
Select A.RN,B.*
From (Select *,RN = Row_Number() over(Order By (Select NULL)) From #Temp) A
Cross Apply [dbo].[udf-Str-Parse](A.Temp,' | ') B
)
-- Insert Into OrderInterface (Name,ID)
Select Name = (Select String1=Stuff((Select ' | ' + RetVal From cte Where RN=A.RN and RetSeq<=7 For XML Path ('')),1,3,'') )
,ID = (Select String1=Stuff((Select ' | ' + RetVal From cte Where RN=A.RN and RetSeq>7 For XML Path ('')),1,3,'') )
From cte A
Group By A.RN
Which Returns
The UDF if Needed
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ Replace(#String,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
I think what you are trying to do is find the 6th element in a string deliminated by |.
If that is the case you should not use a loop (as is always the case in SQL) instead in SQL Server you can use the STRING_SPLIT function (https://msdn.microsoft.com/en-us/library/mt684588.aspx) like this:
SELECT value
FROM STRING_SPLIT(#sql, ' | ')
OFFSET ROWS 5 FETCH FIRST 1 ROW ONLY

Query to split String value into multiple rows

i have
id | dvr
1 | 1,2,3
2 | 1,3,4
3 | 1,5,6,7,8
and would like to have
id | dvr
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
2 | 4
... and so on
what is the fastest query i should use?
Make a sql function as below:
create Function [dbo].[fun_CSVToTable]
(
#LIST varchar(7000),
#Delimeter varchar(10)
)
RETURNS #RET1 TABLE (RESULT BIGINT)
AS
BEGIN
DECLARE #RET TABLE(RESULT BIGINT)
IF LTRIM(RTRIM(#LIST))='' RETURN
DECLARE #START BIGINT
DECLARE #LASTSTART BIGINT
SET #LASTSTART=0
SET #START=CHARINDEX(#Delimeter,#LIST,0)
IF #START=0
INSERT INTO #RET VALUES(SUBSTRING(#LIST,0,LEN(#LIST)+1))
WHILE(#START >0)
BEGIN
INSERT INTO #RET VALUES(SUBSTRING(#LIST,#LASTSTART,#START-#LASTSTART))
SET #LASTSTART=#START+1
SET #START=CHARINDEX(#Delimeter,#LIST,#START+1)
IF(#START=0)
INSERT INTO #RET VALUES(SUBSTRING(#LIST,#LASTSTART,LEN(#LIST)+1))
END
INSERT INTO #RET1 SELECT * FROM #RET
RETURN
END
If you are running postgresql and dvr column is text you could do:
select
id,
unnest(string_to_array(dvr,','))
from your_table;