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

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

Related

Convert and merge two string as a table

Assume we have two below strings:
DECLARE #AllowedCardBoardIds NVARCHAR(MAX) = '1,2,3,4,5,6'
DECLARE #AllowedCardBoardIdsAccessTypes NVARCHAR(MAX) = '11,22,33,44,55,66'
Also, assume we have a function to split a string as a table.
It returns a sequence values
Select * FROM dbo.SplitString(',', #AllowedCardBoardIds)
-- result:
Value
------
1
2
3
4
5
6
Now I want to convert two string to table and merge them as follows:
Id | AccessType
--------------------
1 | 11
2 | 22
3 | 33
4 | 44
5 | 55
6 | 66
How should I do it?
I wrote a query as follows:
DECLARE #AllowedCardBoardsTable TABLE(
Id INT NOT NULL,
AccessType INT NOT NULL
)
INSERT INTO #AllowedCardBoardsTable
(
Id, AccessType
)
SELECT id.[Value], accessType.[Value]
FROM dbo.SplitString(',', #AllowedCardBoardIds) AS id
But I don't know how to fill AccessType column !
Please try the following solution.
SQL
DECLARE #AllowedCardBoardsTable TABLE(
Id INT NOT NULL,
AccessType INT NOT NULL
);
DECLARE #AllowedCardBoardIds NVARCHAR(MAX) = '1,2,3,4,5,6';
DECLARE #AllowedCardBoardIdsAccessTypes NVARCHAR(MAX) = '11,22,33,44,55,66';
DECLARE #separator CHAR(1) = ',';
;WITH rs AS
(
SELECT CardBoardIds = CAST('<root><r><![CDATA[' +
REPLACE(#AllowedCardBoardIds, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
, CardBoardIdsAccessTypes = CAST('<root><r><![CDATA[' +
REPLACE(#AllowedCardBoardIdsAccessTypes, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
)
, rs2 AS
(
SELECT rn = ROW_NUMBER() OVER(ORDER BY (t.c))
, Id = c.value('.', 'INT')
FROM rs
CROSS APPLY CardBoardIds.nodes('/root/r/text()') AS t(c)
)
, rs3 AS
(
SELECT rn = ROW_NUMBER() OVER(ORDER BY (t.c))
, Id = c.value('.', 'INT')
FROM rs
CROSS APPLY CardBoardIdsAccessTypes.nodes('/root/r/text()') AS t(c)
)
INSERT INTO #AllowedCardBoardsTable (Id, AccessType)
SELECT rs2.id, rs3.Id
FROM rs2 INNER JOIN rs3 ON rs3.rn = rs2.rn;
-- test
SELECT * FROM #AllowedCardBoardsTable;
Output
+----+------------+
| Id | AccessType |
+----+------------+
| 1 | 11 |
| 2 | 22 |
| 3 | 33 |
| 4 | 44 |
| 5 | 55 |
| 6 | 66 |
+----+------------+
I would suggest you use one of the built-in string-splitting methods, rather than trying to roll your own.
Unfortunately, current versions of SQL Server do not support STRING_SPLIT with an ordinal column. But in this case you can hack it with OPENJSON
DECLARE #AllowedCardBoardIds NVARCHAR(MAX) = '1,2,3,4,5,6';
DECLARE #AllowedCardBoardIdsAccessTypes NVARCHAR(MAX) = '11,22,33,44,55,66';
SELECT
id = acb.value,
AccessType = at.value
FROM OPENJSON('[' + #AllowedCardBoardIds + ']') acb
JOIN OPENJSON('[' + #AllowedCardBoardIdsAccessTypes + ']') at
ON at.[key] = acb.[key];
db<>fiddle
I strongly suggest you store your data properly normalized in the first place, such as in a table variable, temp table, or normal table.

How to convert comma separated variable values to row in SQL Server 2012?

I have a variable in my stored procedure `#param varchar', the parameter value will be like '333,445,443,222' or '555'
I need to store into as row in the #Employee table, Code column.
Expected output:
#Employee temp table:
Code
----
333
445
443
222
It will work with any SQL server version. User define table function
CREATE FUNCTION dbo.SplitString
(
#Value nvarchar(max),
#Delim nvarchar(5)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT [Value] = LTRIM(RTRIM(SUBSTRING(#Value, [Number],
CHARINDEX(#Delim, #Value + #Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_columns) AS x WHERE Number <= LEN(#Value)
AND SUBSTRING(#Delim + #Value, [Number], DATALENGTH(#Delim)/2) = #Delim
) AS y
);
and then it can be used
DECLARE #param varchar(1000)
SET #param = '333,445,443,222'
SELECT value AS Code FROM dbo.SplitString(#param, ',');
Demo: http://sqlfiddle.com/#!18/9eecb/89486
Since SQL Server 2016, there has been a built-in function to do this:
select s.value
from string_split(#param, ',') s
I would recommend using it. The one downside to string_split() is that it does not provide ordering in the string. If ordering is important and there are no duplicates, then charindex() can be used. If there can be duplicates, then I recommend a recursive CTE.
Your problem can be solved with recursive CTE (need to know about maxrecursion):
create function my_split(#s nvarchar(max), #d nvarchar(1))
returns table return
with
r as (
select
iif(p > 0, left(#s, p - 1), #s) as s,
iif(p > 0, right(#s, len(#s) - p), '') as r
from (select charindex(#d, #s)) as a(p)
union all
select
iif(p > 0, left(r, p - 1), r),
iif(p > 0, right(r, len(r) - p), '')
from r
cross apply (select charindex(#d, r)) as a(p)
where r != ''
)
select s from r;
Output:
+----+-----------------+------+
| | param | code |
+----+-----------------+------+
| 1 | 333,445,443,222 | 333 |
| 2 | 333,445,443,222 | 445 |
| 3 | 333,445,443,222 | 443 |
| 4 | 333,445,443,222 | 222 |
| 5 | 555 | 555 |
+----+-----------------+------+
Demo.

split the accounid and dbids between braces into two columns

I have a query to split the accounid and dbids between braces separately.
example: [14801].[42]
acid scid
14801 42
but I'm not able to separate after comma ,
example :
[27784].[41],[27781].[41],[27779].[41]
need a query to split this data into rows
you need to create function to split values into rows
create function [dbo].[udf_splitstring] (#tokens varchar(max),
#delimiter varchar(5))
returns #split table (
token varchar(200) not null )
as
begin
declare #list xml
select #list = cast('<a>'
+ replace(#tokens, #delimiter, '</a><a>')
+ '</a>' as xml)
insert into #split
(token)
select ltrim(t.value('.', 'varchar(200)')) as data
from #list.nodes('/a') as x(t)
return
end
select * from into #a udf_splitstring ('[27784].[41],[27781].[41],[27779].[41]',',')
output
[27784].[41]
[27781].[41]
[27779].[41]
coming result store in one temp table
SELECT TOKEN,REPLACE(REPLACE(SUBSTRING(TOKEN,0,CHARINDEX('.',TOKEN)),'[',''),']','') AS first_id
,REPLACE(REPLACE(REVERSE(SUBSTRING(REVERSE(TOKEN),0,CHARINDEX('.',REVERSE(TOKEN)))),'[',''),']','') AS second_id
FROM #a
output
TOKEN first_id second_id
[27784].[41] 27784 41
[27781].[41] 27781 41
[27779].[41] 27779 41
Try this:
DECLARE #START_ID VARCHAR(100)='[27784].[41],[27781].[41],[27779].[41]'
DECLARE #ID VARCHAR(MAX)
DECLARE #COUNT INT
DECLARE #TEMP TABLE(C1 VARCHAR(MAX))
WHILE( CHARINDEX(',',#START_ID))>0
BEGIN
INSERT INTO #TEMP SELECT SUBSTRING(#START_ID,0,CHARINDEX(',',#START_ID))
SET #START_ID=(SELECT REPLACE(#START_ID,(SUBSTRING(#START_ID,0,CHARINDEX(',',#START_ID)+1)),''))
END
INSERT INTO #TEMP SELECT #START_ID
SELECT C1,REPLACE(REPLACE(SUBSTRING(C1,0,CHARINDEX('.',C1)),'[',''),']','') AS SOURCE_ACCOUT_ID
,REPLACE(REPLACE(REVERSE(SUBSTRING(REVERSE(C1),0,CHARINDEX('.',REVERSE(C1)))),'[',''),']','') AS SOURCE_DATABASE_ID
FROM #TEMP
select n.ids.value ('id[1]','int') as accountid
,n.ids.value ('id[2]','int') as dbid
from (select cast (replace('<r><e><id>'+replace(replace(replace(
ids,'[',''),']',''),',','</id></e><e><id>')+
'</id></e></r>','.','</id><id>') as xml) as x
from mytable
) t
cross apply x.nodes ('/r/e') n(ids)
+-----------+------+
| accountid | dbid |
+-----------+------+
| 27784 | 41 |
+-----------+------+
| 27781 | 41 |
+-----------+------+
| 27779 | 41 |
+-----------+------+
| 28021 | 30 |
+-----------+------+
| 28024 | 30 |
+-----------+------+
| 29007 | 56 |
+-----------+------+
DDL + DML for demo
create table mytable (ids varchar(1000))
insert into mytable values
('[27784].[41],[27781].[41],[27779].[41]')
, ('[28021].[30],[28024].[30]')
, ('[29007].[56]')

Split sql string value based on 7th delimiter

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.

Convert Decimal to String on Pivot

example Code
Declare
#table1 VARCHAR(MAX)
Set #table1 = 'Select * from #tempTbl'
Declare
#List VARCHAR(MAX),
#Pivot VARCHAR(MAX)
Select #List = ISNULL(#List + ',', '') + TrxCd From TransacMaster Where Module = 'CB'
Set #Pivot = '
SELECT ROW_NUMBER() OVER (ORDER BY DebtorCd ASC) As RowIndex, *
FROM (
Select Distinct
c.UserId,
d.Name,
b.TrxCd
SUM(b.Amount) As Amount
From tbl_InvH a
Inner Join tbl_InvD b on a.subCd = b.subCd and a.InvNo = b.InvNo
Left Join tbl_User c On a.UserId = c.UserId
Left Join tbl_Personnel d on c.PersonnelId = d.PersonnelId
Group By c.UserId, d.Name, b.TrxCd
) as s
PIVOT
(
SUM(Amount)
FOR TrxCd IN (' + #List + ')
)AS pvt'
Declare #Result nVarchar(MAX)
Set #Result = #table1 + '
Union All
' + #Pivot
Exec sp_executesql #Result
I want to Convert Field Amount from decimal to String, because after this I want to UNION with another tables, but field amount and field from another table is different type.
I have tried CAST(SUM(Amount) as Varchar) But Error :
'CAST' is not a recognized aggregate function.
I can't Convert on Main Select because Field of TrxCd is Dynamic
Result Of Pivot
RowIndex | UserId | Name | IT01 | IT02 | IT03 | IT04
--------------------------------------------------------------------------------
1 | John | John Ivy | 2,000 | 2,000 | 1,000 | 5,000
2 | Merry | Merry Ish | 1,000 | 1,000 | 1,000 | 6,000
other Table
RowIndex | UserId | Name | Transac1 | Transac2 | Transac3 | Transac4
-------------------------------------------------------------------------------------------------
1 | John | John Ivy | Trx Bank A | Trx Bank B | Trx Bank C | Trx Bank D
What should I do to Convert Field Amount from Pivot.
Because of the dynamic nature, you could take the same #List expression Select #List = ISNULL(#List + ',', '') + TrxCd From TransacMaster Where Module = 'CB' and create a second for the dynamic casting in the main select;
Select #List2 = ISNULL(#List2 + ',', '') + 'cast(' + TrxCd + 'as varchar)' From TransacMaster Where Module = 'CB'
Then use this and additional columns required to replace the asterix in the main select;
'SELECT ROW_NUMBER() OVER (ORDER BY DebtorCd ASC) As RowIndex, UserId, Name,' + #List2 +'....'