DATA:
id data
114 10047,10001,10003
123 90080
233 10020,10029,10032,10065
TABLE:
DECLARE #TEMP TABLE (id int
,data varchar(50)
PRIMARY KEY (id)
)
INSERT INTO #TEMP VALUES (114,'10047,10001,10003')
INSERT INTO #TEMP VALUES (123,'90080')
INSERT INTO #TEMP VALUES (233,'10020,10029,10032,10065')
Given the above data I am trying to convert the comma separated values to a table so I can join the values, I already have a function that works fine by taking a single value as a parameter, how do I call the function so that it goes through each of the rows above (without using cursors) and outputs the values as such :
id data
114 10047
114 10001
114 10003
123 90080
233 10020
233 10029
233 10032
233 10065
Function:
FUNCTION [dbo].[ConvertCsvToTable](#csvList nvarchar(MAX))
RETURNS #csvTable table([id] int NOT NULL)
AS
BEGIN
DECLARE #pos int
DECLARE #nextPos int
DECLARE #valueLen int
SELECT #pos = 0, #nextPos = 1
WHILE #nextPos > 0
BEGIN
SELECT #nextPos = charindex(',', #csvList, #pos + 1)
SELECT #valueLen = CASE WHEN #nextPos > 0
THEN #nextPos
ELSE len(#csvList) + 1
END - #pos - 1
INSERT #csvTable (id)
VALUES (convert(int, substring(#csvList, #pos + 1, #valueLen)))
SELECT #pos = #nextPos
END
RETURN
END
You can use simplier solution if you convert your comma separated values to XML data. Your result could be achieved with a single select statement like as follows:
-- Improve table structure adding XML field
DECLARE #TEMP TABLE (id int
,data varchar(50)
,xmldata xml
PRIMARY KEY (id)
)
-- Your initial data
INSERT INTO #TEMP VALUES (114,'10047,10001,10003',null)
INSERT INTO #TEMP VALUES (123,'90080',null)
INSERT INTO #TEMP VALUES (233,'10020,10029,10032,10065',null)
-- Transforming CSV string to XML
UPDATE #TEMP
SET xmldata = CONVERT(XML, '<DATA><RAW>'+REPLACE(data, ',', '</RAW><RAW>')+'</RAW></DATA>')
-- Query XML data to obtain required result
SELECT t.id,
x.xmldata.value('(.)[1]','INT') AS data
FROM #TEMP t
CROSS APPLY t.xmldata.nodes('/DATA/RAW') x(xmldata);
Related
ID NAME TYPE
1 ABC 1,2,3,4
2 PQR 2,3,5
3 XYZ 1,4
4 TCS 3,1
5 PPP 2,3
I want output like this
ID NAME TYPE
1 ABC 1
2
3
4
2 pqr 2
3
5
and so on
There are two basic challenges with this; splitting the comma separated list of values and then only displaying the first instance of ID and NAME. There may be more efficient or elegant ways to achieve this. However, I have chosen a brute force approach using a cursor. Here you go.
IF OBJECT_ID('tempdb..#Temp', 'U') IS NOT NULL DROP TABLE #Temp;
IF OBJECT_ID('tempdb..#Results', 'U') IS NOT NULL DROP TABLE #Results;
CREATE TABLE #Temp (
ID INT
, NAME CHAR(3)
, TYPE VARCHAR(10)
)
CREATE TABLE #Results (
ID VARCHAR(3)
, NAME CHAR(3)
, TYPE VARCHAR(10)
)
INSERT INTO #Temp (ID, NAME, TYPE) VALUES (1, 'ABC', '1,2,3,4')
INSERT INTO #Temp (ID, NAME, TYPE) VALUES (2, 'PQR', '2,3,5')
INSERT INTO #Temp (ID, NAME, TYPE) VALUES (3, 'XYZ', '1,4')
INSERT INTO #Temp (ID, NAME, TYPE) VALUES (4, 'TCS', '3,1')
INSERT INTO #Temp (ID, NAME, TYPE) VALUES (5, 'PPP', '2,3')
DECLARE #First BIT
DECLARE #ValueList VARCHAR(100)
DECLARE #pos INT
DECLARE #len INT
DECLARE #value VARCHAR(100)
/* declare cursor variables */
DECLARE #ID INT
DECLARE #NAME CHAR(3)
DECLARE #TYPE VARCHAR(10)
DECLARE MyCursor CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT * FROM #Temp
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO #ID, #NAME, #TYPE
WHILE ##FETCH_STATUS = 0
BEGIN
SET #ValueList = #TYPE + ','
SET #pos = 0
SET #len = 0
SET #First = 1
WHILE CHARINDEX(',', #ValueList, #pos + 1) > 0
BEGIN
SET #len = CHARINDEX(',', #ValueList, #pos + 1) - #pos
SET #value = SUBSTRING(#ValueList, #pos, #len)
SET #pos = CHARINDEX(',', #ValueList, #pos + #len) + 1
IF (#First = 1)
BEGIN
INSERT INTO #Results (ID, NAME, TYPE) VALUES (#ID, #NAME, #value)
END
ELSE
BEGIN
INSERT INTO #Results (ID, NAME, TYPE) VALUES ('', '', #value)
END
SET #First = 0
END
FETCH NEXT FROM MyCursor INTO #ID, #NAME, #TYPE
END
CLOSE MyCursor
DEALLOCATE MyCursor
SELECT * FROM #Results
Enjoy,
Noel
I have two comma separated list which I know belong to a single ID, so I need to insert each corresponding value from the two comma separated list into the table with the common ID.
I have #listA, #listB
and their values would be
#listA ='BRE,CT,DIA,DEXA'
#listB ='2.00,3.00,4.00,5.00'
and I know the ID to which these values belong, say
#SpecID=1
Now what I want is to insert data into a table which contains all three columns like this
Please provide me the steps to approach this problem.
Step 1: I have created the below Function that accepts the Comma separated list items and a Delimiter. It returns the split values along with a RowNumber.
CREATE FUNCTION dbo.SplitString
(
#Delimiter VARCHAR(1),
#InputString VARCHAR(MAX)
)
RETURNS
#ListItems TABLE
(
RowNumber INT,
List VARCHAR(50)
)
AS
BEGIN
DECLARE #Position INT, #RowNumber INT = 1, #ListItem VARCHAR(MAX)
WHILE CHARINDEX(#Delimiter, #InputString) > 0
BEGIN
SELECT #Position = CHARINDEX(#Delimiter, #InputString)
SELECT #ListItem = SUBSTRING(#InputString, 1, #Position-1)
INSERT INTO #ListItems
SELECT #RowNumber,#ListItem
SELECT #InputString = SUBSTRING(#InputString, #Position+1, LEN(#InputString)-#Position)
SET #RowNumber = #RowNumber + 1
END
INSERT INTO #ListItems
Select #RowNumber,#InputString
RETURN
END
Step 2: Using the above function, I split the comma separated list items and created 2 table variables.
DECLARE #listA VARCHAR(MAX), #listB VARCHAR(MAX), #SpecID INT, #Delimiter VARCHAR(1)
SET #listA= 'BRE,CT,DIA,DEXA'
SET #listB ='2.00,3.00,4.00,5.00'
SET #SpecID = 1
SET #Delimiter = ','
DECLARE #ListItems Table
(
SpecID INT,
listA VARCHAR(50),
listB VARCHAR(50)
)
DECLARE #TableListA Table
(
RowNumber INT,
ListA VARCHAR(50)
)
DECLARE #TableListB Table
(
RowNumber INT,
ListB VARCHAR(50)
)
INSERT INTO #TableListA
SELECT * FROM SplitString(#Delimiter,#listA)
INSERT INTO #TableListB
SELECT * FROM SplitString(#Delimiter,#listB)
INSERT INTO #ListItems
SELECT
#SpecID,
A.ListA,
B.ListB
FROM #TableListA A
INNER JOIN #TableListB B ON B.RowNumber = A.RowNumber
SELECT * FROM #ListItems
Please use the SQL Fiddle to check the output: http://sqlfiddle.com/#!6/9e12b/1/0
You will have to do it like this:
1) Split using `,`
2) Have 2 arrays
3) Both need to have the same size so iterate over 1 array
4) Use the index within the loop to insert the values.
I have a stored procedure In which insert query fire on two table.
masters_table,child_table as you see here #Value1,#Value2,#Value3,#Value4 having parameter. I have written insert statement for each block, can I make use loop here to avoid multiple insert write for each block, suppose I have nth value then i have to write nth time block wise insert query, any better way to achieve this task
Create proc sp_insert_master_child
(
#Details_Data custom_tb READONLY,
#Value1 nvarchar(500),
#Value2 nvarchar(500),
#Value3 nvarchar(500),
#Value4 nvarchar(500),
#Value5 nvarchar(500),
#Value6 nvarchar(500),
#Value7 nvarchar(500)
)
as begin
declare #id bigint
------- block 1
if #Value1 is not null
begin
insert into masters_table (col1,col2,col3))
select Substring(#Value1, 1,Charindex(',', #Value1)-1) as col_value_1,
Substring(#Value1, Charindex(',', #Value1)+1, LEN(#Value1)) as col_value_2
select #id = ##IDENTITY
insert into child_table (col1,col2,col3)
SELECT #id,col_val_2,col_val_3 FROM #Details_Data WHERE blockType='blk_1';
end
------ block 2
if #Value2 is not null
begin
insert into masters_table (col1,col2,col3))
select Substring(#Value2, 1,Charindex(',', #Value2)-1) as col_value_1,
Substring(#Value2, Charindex(',', #Value2)+1, LEN(#Value2)) as col_value_2
select #id = ##IDENTITY
insert into child_table (col1,col2,col3)
SELECT #id,col_val_2,col_val_3 FROM #Details_Data WHERE blockType='blk_2';
end
------ block 3
if #Value3 is not null
begin
insert into masters_table (col1,col2,col3))
select Substring(#Value3, 1,Charindex(',', #Value3)-1) as col_value_1,
Substring(#Value3, Charindex(',', #Value2)+1, LEN(#Value3)) as col_value_2
select #id = ##IDENTITY
insert into child_table (col1,col2,col3)
SELECT #id,col_val_2,col_val_3 FROM #Details_Data WHERE blockType='blk_3';
end
------ block 4
.......
------ block 5
.......
You can insert all parameters into a table .Use while loop and read one by one row (parameter) from table .
Ex:
declare #x int
set #x= 1
declare #tmp table ( val int )
while (#x < noofrows)
begin
***********
Use Top #x value to get row(parameter in your case) from table
perform your operation
*************
increment x(set #X=#x+1)
end
I have a table with
Table name TB1
mpeFromWHId mpeToStoreList
8 16,18,24
and Table tb2 are the codes of the comma separated storeid
nlid nlcode
16 ncl
18 mcl
24 dcl
I need a query that will result in
col1 Col2
8 ncl,mcl,dcl
First you need a function to parse comma delimited string into table, you can use this (found [here])1:
CREATE FUNCTION [dbo].Split1(#input AS Varchar(4000) )
RETURNS
#Result TABLE(Value BIGINT)
AS
BEGIN
DECLARE #str VARCHAR(20)
DECLARE #ind Int
IF(#input is not null)
BEGIN
SET #ind = CharIndex(',',#input)
WHILE #ind > 0
BEGIN
SET #str = SUBSTRING(#input,1,#ind-1)
SET #input = SUBSTRING(#input,#ind+1,LEN(#input)-#ind)
INSERT INTO #Result values (#str)
SET #ind = CharIndex(',',#input)
END
SET #str = #input
INSERT INTO #Result values (#str)
END
RETURN
END
Then you can use something like this (but there are many more options off course):
declare #searchId int
set #searchId = 8
declare #tb1 table (mpeFromWHId int, mpeToStoreList varchar(100))
insert into #tb1
select 8, '16,18,24'
declare #tb2 table (nlid int, nlcode varchar(30))
insert into #tb2
select 16, 'ncl' union
select 18, 'mcl' union
select 24, 'dcl'
select stuff((
select ',' + nlcode
from #tb2
where nlid in (
select Value
from dbo.Split1((select mpeToStoreList from #tb1 where mpeFromWHId = #searchId))
)
order by nlcode
for xml path(''), type
).value('(./text())[1]','varchar(max)'), 1, 2, '')
If you don't want to create a user function, you can do like this:
;with TB1(mpeFromWHId, mpeToStoreList) AS (
select 8,'16,18,24'
)
SELECT t.mpeFromWHId,n.ID FROM (
select *,convert(XML,'<n>'+replace(mpeToStoreList,',','</n><n>')+'</n>') as x from tb1
) AS t
cross apply(select s.b.value('.','INT') as ID from t.x.nodes('n') s(b)) n
mpeFromWHId ID
8 16
8 18
8 24
I have a table named assignRole.
I am passing string of userid (int) csv ,and passing roleid(int).
I want a stored procedure which split userid from string and take roleid and insert these values in table.this thing is to happen for all values in userid string.
First, create a function:
CREATE FUNCTION [dbo].[SplitInts]
(
#List VARCHAR(MAX),
#Delimiter CHAR(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN ( SELECT Item = CONVERT(INT, Item) FROM (
SELECT Item = x.i.value('(./text())[1]', 'int') FROM (
SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y
WHERE Item IS NOT NULL
);
Now you can say:
INSERT dbo.assignRole(RoleID, UserID)
SELECT #RoleID, Item
FROM dbo.SplitInts(#UserIDList, ',');
I like to use a table-valued function to do the split.
IF OBJECT_ID (N'dbo.StrSplit') IS NOT NULL DROP FUNCTION dbo.[StrSplit]
GO
CREATE FUNCTION [dbo].[StrSplit]
(
#String VARCHAR(MAX), #Delimiter char(1)
)
RETURNS
#Results TABLE (
Items NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(MAX)
-- HAVE TO SET TO 1 SO IT DOESNT EQUAL ZERO FIRST TIME IN LOOP
SELECT #INDEX = 1
-- Early exit if passed string is null
IF #String IS NULL RETURN
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results(Items) VALUES(#SLICE)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
GO
GRANT SELECT ON [dbo].[StrSplit] TO [public]