I need to separate values and store them in different variables in SQL,
for example
a='3100,3101,3102,....'
And the output should be
x=3100
y=3101
z=3102
.
.
.
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
GO
declare #cad varchar(100)='3100,3101,3102'
select *,ROW_NUMBER() over (order by token ) as rn from udf_splitstring(#cad,',')
token rn
3100 1
3101 2
3102 3
The results of the Parse TVF can easily be incorporated into a JOIN, or an IN
Declare #a varchar(max)='3100,3101,3102'
Select * from [dbo].[udf-Str-Parse](#a,',')
Returns
RetSeq RetVal
1 3100
2 3101
3 3102
The UDF if needed (much faster than recursive, loops, and xml)
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(25))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter)) = #Delimiter),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By A.N)
,RetVal = LTrim(RTrim(Substring(#String, A.N, A.L)))
From cte4 A
);
--Orginal Source http://www.sqlservercentral.com/articles/Tally+Table/72993/
--Much faster than str-Parse, but limited to 8K
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-8K]('John||Cappelletti||was||here','||')
I suggest you to use following query, it's much faster than other functions like cross apply and udf.
SELECT
Variables
,S_DATA
FROM (
SELECT
Variables
,CASE WHEN LEN(LIST2)>0 THEN LTRIM(RTRIM(SUBSTRING(LIST2, NUMBER+1, CHARINDEX(',', LIST2, NUMBER+1)-NUMBER - 1)))
ELSE NULL
END AS S_DATA
,NUMBER
FROM(
SELECT Variables
,','+COMMA_SEPARETED_COLUMN+',' LIST2
FROM Tb1
)DT
LEFT OUTER JOIN TB N ON (N.NUMBER < LEN(DT.LIST2)) OR (N.NUMBER=1 AND DT.LIST2 IS NULL)
WHERE SUBSTRING(LIST2, NUMBER, 1) = ',' OR LIST2 IS NULL
) DT2
WHERE S_DATA<>''
and also you should create a table 'NUMBER' before running the above query.
CREATE TABLE TB (Number INT)
DECLARE #I INT=0
WHILE #I<1000
BEGIN
INSERT INTO TB VALUES (#I)
SET #I=#I+1
END
Related
I am trying to fill db pre populated data from sql script where I have two type of constants or enums.
Platform: DG, NK
Department : KK, TG, LO, NP, UI, BG, ED, CC.
Task: To generate a sequential number using procedural loop and for each combination using above value we need to generate key and put in data base with count or sequence value.
Database columns are based on JPA abstract entity:
id, created_by, created_at, status, updated_by, updated_at, uuid, count, category_key
Now single row would be one combination which is formed using this pattern,
Department_Platform_SequenceNumber :: example => KK_DG_1,....KK_DG_10000, KK_NK_1,....KK_NK_10000
This is for 10k entries for 10k sequence of each combinations. It follows for other as well.
Approach:
DROP PROCEDURE KeyGeneration;
DELIMITER $$
CREATE PROCEDURE LoopDemo()
BEGIN
DECLARE x INT;
DECLARE dep VARCHAR(10);
DECLARE plat VARCHAR(10);
DECLARE str VARCHAR(30);
SET x = 1;
SET dep = 'KK'; # help to initialize enums or constants
SET plat = 'DG'; # help to initialize enums or constants
count_val: LOOP
IF x=10000 THEN
LEAVE count_val;
END IF;
SET str = CONCAT(dep,'_',plat,'_',x);
insert into counter_key values(id, created_by, created_at, status, updated_by, updated_at, uuid, x, str);
SET x = x + 1;
END LOOP;
END$$
DELIMITER ;
call LoopDemo();
but this is wrong since what I want is that atleast id to be updated if possible created_at and other fields as well also the loop will return last value I guess I want to get each value.
TABLE COLUMNS UPDATED
UPDATED: id, count, counter_key, status
#Akina answer applied as per my new table but syntax error
WITH RECURSIVE
number AS ( SELECT 1 number
UNION ALL
SELECT number + 1 FROM cte WHERE number < 10000 ),
platform AS ( SELECT 'DG' platform
UNION ALL
SELECT 'NK' ),
department AS ( SELECT 'KK' department
UNION ALL
SELECT 'TG'
UNION ALL
SELECT 'LO'
UNION ALL
SELECT 'NP'
UNION ALL
SELECT 'UI'
UNION ALL
SELECT 'BG'
UNION ALL
SELECT 'ED'
UNION ALL
SELECT 'CC' )
INSERT INTO counter_key
SELECT null, number, CONCAT_WS('_', department, platform, number), 1
FROM department
CROSS JOIN platform
CROSS JOIN number;
WITH RECURSIVE
number AS ( SELECT 1 number
UNION ALL
SELECT number + 1 FROM cte WHERE number < 10000 ),
platform AS ( SELECT 'DG' platform
UNION ALL
SELECT 'NK' ),
department AS ( SELECT 'KK' department
UNION ALL
SELECT 'TG'
UNION ALL
SELECT 'LO'
UNION ALL
SELECT 'NP'
UNION ALL
SELECT 'UI'
UNION ALL
SELECT 'BG'
UNION ALL
SELECT 'ED'
UNION ALL
SELECT 'CC' )
INSERT INTO counter_key
SELECT #id, #created_by, #created_at, #status, #updated_by, #updated_at, #uuid, #x,
CONCAT_WS('_', department, platform, number)
FROM department
CROSS JOIN platform
CROSS JOIN number;
where #id, #created_by, #created_at, #status, #updated_by, #updated_at, #uuid, #x must be replaced with literal values taken from somewhere.
How do I accomplish my goal without using temp table or variable?
Table:
ID ModelNum Qty
123 ABC 4
123 DEF 4
Expected Result:
ID Models Qty
123 ABC | DEF 4
Thanks in advance!
DECLARE #T TABLE (ID INT,ModelNum CHAR(3),Qty INT)
INSERT INTO #T
VALUES
(123,'ABC',4),
(123,'DEF',4),
(123,'GLK',4)
SELECT DISTINCT ID, STUFF(C.List, 1, 2, '') Models, Qty
FROM #T t
CROSS APPLY (
SELECT '| ' + ModelNum
FROM #T
WHERE ID = t.ID
FOR XML PATH('')
)C(List)
Result Set
ID Models Qty
123 ABC| DEF| GLK 4
Hi how about this Query below:
I have did the same example with some different logic and different attribute.
I can get the expected OP, please response if you have any suggestions for me on btechit#hotmail.com.
Declare:
#ConcatTable table (Ename varchar(30), Empno int)
Insert into #ConcatTable values ('Steve', 100),('mathew', 100),('jon', 101),('tom', 101)
--select * from #ConcatTable
--select ROW_NUMBER()over(order by Empno)Row2,* from
--(select distinct Empno from #ConcatTable)C
declare #p varchar(100) = ''
select #p = #p+ ' '+Ename from (
select DENSE_RANK()over(order by Empno)as dens, * from #ConcatTable )A
where A.dens = 1
declare #q varchar(100) = ''
select #q = #q+ ' '+Ename from (
select DENSE_RANK()over(order by Empno)as dens, * from #ConcatTable )A
where A.dens = 2
--SELECT #p
--SELECT #q
declare #M table (Name varchar(30))
insert into #M
select * from(
select #p as v
union all
select #q as vv
)G
--SELECT ROW_NUMBER()over (order by Name desc)Rown1,* from #M
SELECT A.Name,CC.Empno FROM(
SELECT ROW_NUMBER()over (order by Name desc)Rown1,* FROM #M)A
inner join
(select ROW_NUMBER()over(order by Empno)Row2,* from
(select distinct Empno from #ConcatTable)C
)CC
on A.Rown1 = CC.Row2
Day #3 with SQL Server.
I am trying to combine 2 columns of delimited data into one output from a Table Valued Function. Here is my data:
I would like the data to be processed and placed into a table in the following format:
I am currently trying to use this CROSS APPLY TSQL statement, but I don't know what I'm doing.
USE [Metrics]
INSERT INTO dbo.tblSplitData(SplitKey, SplitString, SplitValues)
SELECT d.RawKey, c.*, e.*
FROM dbo.tblRawData d
CROSS APPLY dbo.splitstringcomma(d.DelimitedString) c, dbo.splitstringcomma(d.DelimitedValues) e
My research on CROSS APPLY has broad context, and I don't understand how it should be applied in this scenario. Do I need a subquery with an additional CROSS APPLY and a join to combine the returns from the two Table Valued Functions?
Here is the split function I was using originally (I can't remember the author to credit them):
CREATE FUNCTION [dbo].[splitstring] ( #stringToSplit VARCHAR(MAX), #Delimiter CHAR(1))
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(#Delimiter, #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(#Delimiter, #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END
Edit & Revised Query
USE [Metrics]
INSERT INTO dbo.tblSplitData(SplitKey, SplitString, SplitValues)
SELECT s.RawKey, s.SplitString, v.SplitValues
FROM (
SELECT d.RawKey, d.DelimitedString,
c.item SplitString, c.rn
FROM dbo.tblRawData d
CROSS APPLY dbo.splitstring(d.DelimitedString, ',') c
) s
INNER JOIN
(
SELECT d.RawKey, d.DelimitedValues,
c.item SplitValues, c.rn
FROM dbo.tblRawData d
CROSS APPLY dbo.splitstring(d.DelimitedValues, ',') c
) v
on s.RawKey = v.RawKey
and s.rn = v.rn;
It might be easier to answer this if we could see your split string function. My answer is using a version of my split function that I have.
I would include in your split function a row number that you can use to JOIN the split string and the split values.
Split function:
CREATE FUNCTION [dbo].[Split](#String varchar(MAX), #Delimiter char(1))
returns #temptable TABLE (items varchar(MAX), rn int)
as
begin
declare #idx int
declare #slice varchar(8000)
declare #rn int = 1 -- row number that increments with each value in the delimited string
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items, rn) values(#slice, #rn)
set #String = right(#String,len(#String) - #idx)
set #rn = #rn +1
if len(#String) = 0 break
end
return
end;
Then if you have multiple columns to split, you could use a query similar to the following:
INSERT INTO dbo.tblSplitData(SplitKey, SplitString, SplitValues)
select s.rawkey,
s.splitstring,
v.splitvalues
from
(
SELECT d.RawKey, d.delimitedstring, d.delimitedvalues,
c.items SplitString,
c.rn
FROM dbo.tblRawData d
CROSS APPLY dbo.Split(d.DelimitedString, ',') c
) s
inner join
(
SELECT d.RawKey, d.delimitedstring, d.delimitedvalues,
c.items SplitValues,
c.rn
FROM dbo.tblRawData d
CROSS APPLY dbo.Split(d.DelimitedValues, ',') c
) v
on s.rawkey = v.rawkey
and s.delimitedstring = v.delimitedstring
and s.rn = v.rn;
See SQL Fiddle with Demo
This uses two subqueries that generate the list of split values, then they are joined using the row number created by the split function.
Since you are on Sql Server 2008 .You can do this without a UDF using XML.
;WITH CTE1 AS
(
SELECT *
,RN= Row_Number() OVER( Partition BY DelemitedString,DelimitedValues,RawKey,TableID ORDER BY TableID)
FROM
(
SELECT *
,DelimitedStringXML = CAST('<d>'+REPLACE(DelemitedString,',','</d><d>')+'</d>' AS XML)
,DelimitedValueXML = CAST('<d>'+REPLACE(DelimitedValues,',','</d><d>')+'</d>' AS XML)
FROM dbo.tblRawData
) as t
Cross Apply
(
SELECT y.value('.', 'VARCHAR(30)') AS SplitString FROM DelimitedStringXML.nodes('//d') as x(y)
) as b
)
,CTE2 AS
(
SELECT *
,RN= Row_Number() OVER( Partition BY DelemitedString,DelimitedValues,RawKey,TableID ORDER BY TableID)
FROM
(
SELECT *
,DelimitedStringXML = CAST('<d>'+REPLACE(DelemitedString,',','</d><d>')+'</d>' AS XML)
,DelimitedValueXML = CAST('<d>'+REPLACE(DelimitedValues,',','</d><d>')+'</d>' AS XML)
FROM dbo.tblRawData
) as t
CROSS APPLY
(
SELECT h.value('.', 'VARCHAR(30)') AS SplitValue FROM DelimitedValueXML.nodes('//d') as g(h)
) as c
)
SELECT a.RawKey,a.SplitString,b.SplitValue
FROM CTE1 as a
INNER JOIN CTE2 as b
on a.TableID= b.TableID
AND a.RN = b.RN
Here is SQLFiddle Demo
I have 2 tables, T1 and T2. I want to join these 2 tables and return only 2 rows of data, replacing the integers in Item with their lookup values from T2.
Table T1
Item Date
------ ---------
1;4;5; 3/13/2013
1;2;3; 3/13/2013
Table T2
ID Desc
---- ------
1 Tree
2 Grass
3 Sand
4 Water
5 Bridge
Expected results:
Item Date
------------------ ---------
Tree;Water;Bridge; 3/13/2013
Tree;Grass;Sand; 3/13/2013
First, create a Split function which returns an integer and an order-preserving sequence number. Here is one example:
ALTER FUNCTION dbo.SplitInts
(
#List VARCHAR(MAX),
#Delimiter VARCHAR(32)
)
RETURNS TABLE
AS
RETURN
(
SELECT rn = ROW_NUMBER() OVER (ORDER BY Number),
Item = CONVERT(INT, Item)
FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(#List, Number,
CHARINDEX(#Delimiter, #List + #Delimiter, Number) - Number)))
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects) AS n(Number)
WHERE Number <= CONVERT(INT, LEN(#List))
AND SUBSTRING(#Delimiter + #List, Number, 1) = #Delimiter
) AS y
);
GO
Then the following query does what you're after:
DECLARE #t1 TABLE
(
Item VARCHAR(MAX),
[Date] DATE -- terrible column name!
);
INSERT #t1 VALUES('1;4;5;','20130313'),('1;2;3;','20130313');
-- please use unambiguous date formats!
DECLARE #t2 TABLE
(
ID INT, -- another bad column name - what kind of ID?
[Desc] VARCHAR(255) -- another bad column name, this is a keyword!
);
INSERT #t2 VALUES(1,'Tree'),(2,'Grass'),
(3,'Sand'),(4,'Water'),(5,'Bridge');
;WITH x AS
(
SELECT t1.Item, Date, t2ID = i.Item, i.rn, n = t2.[Desc]
FROM #t1 AS t1 CROSS APPLY dbo.SplitInts(t1.Item, ';') AS i
INNER JOIN #t2 AS t2 ON i.Item = t2.ID
)
SELECT DISTINCT Item = (
SELECT n + ';' FROM x AS x2
WHERE x.Item = x2.Item
ORDER BY x2.rn FOR XML PATH,
TYPE).value(N'./type()[1]', N'varchar(max)'), [Date]
FROM x;
Strongly recommend you research normalization. A semi-colon-separated list is a terrible way to cram together independent values.
I have a table as below
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB'
need output as below but without using any UDF.
Data String
------------
SQL S,Q,L
JOB J,O,B
Please help me on this
Sure you can :). You can make it shorter too...
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB';
With cte as
(
Select Data, Len(Data) DataLength, 1 level
From #t
Union All
Select Data, DataLength - 1, level + 1
From cte
Where DataLength > 1
),
cte2 as
(
Select Data, SUBSTRING(Data, DataLength, 1) DataLetter, level
From cte
),
cte3 as
(
Select Data,
(
SELECT DataLetter + ','
FROM cte2 c
Where c.Data = cte2.Data
Order By level desc
FOR XML PATH(''), TYPE
).value('.[1]', 'NVARCHAR(1000)') DataComa
From cte2
Group By Data
)
Select Data, substring(DataComa, 1, Len(DataComa) - 1) Data2
From cte3
Late to the party, but here's a slightly shorter version:
DECLARE #T TABLE(Data VARCHAR(MAX));
INSERT INTO #T VALUES('SQL'),('JOB'),('FLOOB');
;WITH n AS (SELECT TOP (SELECT MAX(LEN(Data)) FROM #T)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects
),
t AS (SELECT n, Data, Letter = SUBSTRING(t.Data, n.n, 1) FROM n
INNER JOIN #T AS t ON SUBSTRING(t.Data, n.n, 1) > ''
)
SELECT Data, STUFF((SELECT ',' + letter FROM t AS t2
WHERE t2.Data = t.Data ORDER BY t2.n FOR XML PATH(''),
TYPE).value(N'./text()[1]', N'varchar(max)'), 1, 1, '')
FROM t GROUP BY Data;
Results:
FLOOB F,L,O,O,B
JOB J,O,B
SQL S,Q,L
It is very easy to do with UDF.
But If you want with out UDF, the only one way I can think of is
something like this
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB'
select replace(replace(replace(data,'S','S,'),'Q','Q,'),'L','L,') from #T
here you have to replace all the 26 characters with replace function. ie, 'A' with 'A,' 'B' with 'B,' .... 'Z' with 'Z,'
Using the same approach I used for Initcap function here http://beyondrelational.com/modules/2/blogs/70/posts/10901/tsql-initcap-function-convert-a-string-to-proper-case.aspx
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB'
select data,
upper(replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(
' '+data ,
' a','a,'),' b','b,'),'c','c,'),'d','d,'),'e','e,'),'f','f,'),
' g','g,'),' h','h,'),'i','i,'),'j','j,'),'k','k,'),'l','l,'),
' m','m,'),' n','n,'),'o','o,'),'p','p,'),'q','q,'),'r','r,'),
' s','s,'),' t','t,'),'u','u,'),'v','v,'),'w','w,'),'x','x,'),
' y','y,'),' z','z,')) as splitted_data
from
#t