I have one comma separated strings like "1,2,3,4,5,6,7" and I want to insert this values in a table column with other values are constant like
Insert into tbl(value1, value2, value3) values(#v1, #v2, 1)
Insert into tbl(value1, value2, value3) values(#v1, #v2, 2)
Insert into tbl(value1, value2, value3) values(#v1, #v2, 3) etc.
Where #v1,#v2 values are always constant.
So how should I write the query?
In SQL Server 2016+ you can use string_split().
In SQL Server pre-2016, using a CSV Splitter table valued function by Jeff Moden:
create table tbl (value1 int, value2 int, value3 int);
declare #v1 int = 0;
declare #v2 int = -1;
declare #var varchar(8000) = '1,2,3,4,5,6,7';
insert into tbl
select #v1, #v2, s.Item
from delimitedsplit8K(#var, ',') s
select * from tbl;
rextester demo: http://rextester.com/GBVJS38200
returns:
+--------+--------+--------+
| value1 | value2 | value3 |
+--------+--------+--------+
| 0 | -1 | 1 |
| 0 | -1 | 2 |
| 0 | -1 | 3 |
| 0 | -1 | 4 |
| 0 | -1 | 5 |
| 0 | -1 | 6 |
| 0 | -1 | 7 |
+--------+--------+--------+
splitting strings reference:
Tally OH! An Improved SQL 8K “CSV Splitter” Function - Jeff Moden
Splitting Strings : A Follow-Up - Aaron Bertrand
Split strings the right way – or the next best way - Aaron Bertrand
string_split() in SQL Server 2016 : Follow-Up #1 - Aaron Bertrand
In case you need an in-line version
Example
Declare #v1 varchar(25) = 'SomeValue'
Declare #v2 varchar(25) = 'OtherValue'
Declare #S varchar(max)= '1,2,3,4,5,6,7'
Insert Into tbl (value1, value2, value3)
Select #v1,#v2,RetVal
From (
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((Select replace(#S,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) A
The Row Inserted Look Like This
Related
I have a table with rows and in one field there are values like this A,B,C
Table 'Mytable':
|ID | Date | MyValue | SplitID |
|1 | 2019-12-17 | A | |
|2 | 2019-12-15 | A,B | |
|3 | 2019-12-16 | B,C | |
Result should be:
|1 | 2019-12-17 | A | 1 |
|2 | 2019-12-15 | A | 2 |
|4 | 2019-12-15 | B | 2 |
|3 | 2019-12-16 | B | 3 |
|5 | 2019-12-16 | C | 3 |
(Sorry, I could not find HOW to format a table in the Stackoverflow help)
I tried a inline table function which splits the Field Myvalue into more lines but could not pass my rows with
charindex(',',[MyValue])>0
from MyTable as input lines.
The code is this:
ALTER function [dbo].[fncSplitString](#input Varchar(max), #Splitter Varchar(99), #ID int)
returns table as
Return
with tmp (DataItem, ix, ID) as
( select LTRIM(#input) , CHARINDEX('',#Input), #ID --Recu. start, ignored val to get the types right
union all
select LTRIM(Substring(#input, ix+1,ix2-ix-1)), ix2, #ID
from (Select *, CHARINDEX(#Splitter,#Input+#Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem,ID from tmp where ix<>0
Thanks for help
Michael
You can try the following query.
Create table #Temp(
Id int,
DateField Date,
MyValue Varchar(10),
SplitID int
)
CREATE FUNCTION [dbo].[SplitPra] (#Value VARCHAR(MAX), #delimiter CHAR)
RETURNS #DataResult TABLE([Position] TINYINT IDENTITY(1,1),[Value] NVARCHAR(128))
AS
BEGIN
DECLARE #XML xml = N'<r><![CDATA[' + REPLACE(#Value, #delimiter, ']]></r><r><![CDATA[') + ']]></r>'
INSERT INTO #DataResult ([Value])
SELECT RTRIM(LTRIM(T.c.value('.', 'NVARCHAR(128)')))
FROM #xml.nodes('//r') T(c)
RETURN
END
insert into #Temp Values(1, '2019-12-17', 'A', NULL),(2, '2019-12-15', 'A,B', NULL), (3, '2019-12-16', 'B,C', NULL)
Select
#Temp.Id, DateField, b.Value as MyValue, b.Id as SplitValue
from #Temp inner join (
select
Id, f.*
from
#Temp u
cross apply [dbo].[SplitPra](u.MyValue, ',') f
)b on #Temp.Id = b.Id
Drop table #Temp
This will give an output as shown below.
Id DateField MyValue SplitValue
---------------------------------
1 2019-12-17 A 1
2 2019-12-15 A 2
2 2019-12-15 B 2
3 2019-12-16 B 3
3 2019-12-16 C 3
You can find the live demo here.
I found this solution, i hope it will work for you. But i didn't use your function to solve this problem. Instead of that, i used cross apply function.You can find the query below:
-- Creating Test Table
CREATE TABLE #Test
(
ID int,
Date date,
MyValue nvarchar(max),
SplitID int
);
GO
-- Inserting data into test table
INSERT INTO #Test VALUES (1, '2019-12-17', 'A', NULL);
INSERT INTO #Test VALUES (2, '2019-12-15', 'A,B', NULL);
INSERT INTO #Test VALUES (3, '2019-12-16', 'B,C', NULL);
GO
-- Select query
SELECT
*,
(SELECT ID FROM test t1 WHERE t.Date = t1.date) AS SplitID
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS ID,
Date,
substring(A.value,1,
CASE WHEN charindex(',',rtrim(ltrim(A.value))) = 0 then LEN(A.value)
ELSE charindex(',',rtrim(ltrim(A.value))) -1 end) as MyValue
FROM Test
CROSS APPLY string_split (MyValue, ',') A) AS T
ORDER BY MyValue ASC;
And the result must be like that:
ID Date MyValue SplitID
1 2019-12-17 A 1
2 2019-12-15 A 2
3 2019-12-15 B 2
4 2019-12-16 B 3
5 2019-12-16 C 3
I have a query that returns data in the form
[SetID],[COLID],[DataValue],[Order].
1,1,'Value1',1
1,1,'Value2',2
1,1,'Value3',3
1,2,'value4',1
1,2,'Value5',2
1,2,'Value6',3
How to make it return the data like
[SetID],'Value1','Value4',1
[SetID],'Value2','Value5',2
[SetID],'Value3','Value6',3
So that the data tagged with each columnid is back into a column.
The answer appears to be some sort of pivot, but I'm not looking for a straight conversion, or a one-row answer.
To be able to put each data value into a separate column, you need to know the number of DataValues and the ColId of each. You can't dynamically return variable number of columns (unless you use dynamic SQL to construct the query on the fly), moreover each row needs to have the same number of columns, otherwise the table structure doesn't make sense.
Here's an example that you can quickly test, it uses an in-memory table.
Given the table MyTable:
declare #MyTable table (SetId int, ColId int, DataValue varchar(20), [Order] int);
insert into #MyTable values (1,1,'Value1',1)
insert into #MyTable values (1,1,'Value2',2)
insert into #MyTable values (1,1,'Value3',3)
insert into #MyTable values (1,2,'Value4',1)
insert into #MyTable values (1,2,'Value5',2)
insert into #MyTable values (1,2,'Value6',3)
This query will return the values in separate columns:
select mt.SetId,
( select top 1 m1.DataValue from #MyTable m1
where m1.SetId = mt.SetId and
m1.[Order] = mt.[Order] and
m1.ColId = 1 -- hard-coded ColId, you need to know its value
) 'val1',
( select top 1 m1.DataValue from #MyTable m1
where m1.SetId = mt.SetId and
m1.[Order] = mt.[Order] and
m1.ColId = 2 -- same as above
) 'val2', mt.[Order]
from #MyTable mt
group by mt.SetId, mt.[Order]
Result:
+-----------------------------------+
| SetId | Val1 | Val2 | Order |
|-------+---------+--------+--------|
| 1 | Value1 | Value4 | 1 |
|-------+---------+--------+--------|
| 1 | Value2 | Value5 | 2 |
|-------+---------+--------+--------|
| 1 | Value3 | Value6 | 3 |
+-----------------------------------+
However, I would recommend stuffing all the data values into one column (separated by a delimiter), because this will work with any number of data values. Here's the query:
select mt.SetId,
stuff(( select ', '+ m2.DataValue
from #MyTable m2
where mt.SetId = m2.SetId and
mt.[Order] = m2.[Order]
FOR XML PATH('')) , 1, 2, '') 'DataValues',
mt.[Order]
from #MyTable mt
group by mt.SetId, mt.[Order]
Result
+----------------------------------+
| SetId | DataValues | Order |
|-------+------------------+-------|
| 1 | Value1, Value4 | 1 |
|-------+------------------+-------|
| 1 | Value2, Value5 | 2 |
|-------+------------------+-------|
| 1 | Value3, Value6 | 3 |
+-------+------------------+-------+
This is salar fuction . I am using this in Stored procedure with muliple parameters. For small results it is good but for big data it gets time out and also taking a long execuition time. Please share some other alternate or some enhancements
ALTER FUNCTION [dbo].[FNGETMULIPLEASSIGNESS_NEW2]
(
#TIMELINEID INT,
#MILSTONEID INT,
#TASKID INT
)
RETURNS varchar(max)
AS
BEGIN
DECLARE #Assignees varchar(max)='', #isExists bit=0
if(#TASKID=0)
BEGIN
Select #Assignees = #Assignees+ FIRSTNAME +' ' + LASTNAME+', '
FROM CASETIMELINEPEOPLE
INNER JOIN USERDETAIL ON
CASETIMELINEPEOPLE.PEOPLEUSERID=USERDETAIL.USERID
WHERE (CASETIMELINEID= #TIMELINEID) AND
(TEMPLATEMILESTONEID=#MILSTONEID) AND
(TEMPLATETASKID is null) and CASETIMELINEPEOPLE.isdeleted=0
END
else
BEGIN
Select #Assignees = #Assignees+ FIRSTNAME +' ' + LASTNAME+','
FROM CASETIMELINEPEOPLE
INNER JOIN USERDETAIL ON
CASETIMELINEPEOPLE.PEOPLEUSERID=USERDETAIL.USERID
WHERE (CASETIMELINEID= #TIMELINEID) AND
(TEMPLATEMILESTONEID=#MILSTONEID) AND
(TEMPLATETASKID=#TASKID) and CASETIMELINEPEOPLE.isdeleted=0
END
SELECT #Assignees=SUBSTRING(#Assignees, 0,LEN(#Assignees))
RETURN #Assignees
END
Using an inline table valued function will improve performance.
Reference:
When is a SQL function not a function? "If it’s not inline, it’s rubbish." - Rob Farley
Inline Scalar Functions - Itzik Ben-Gan
Scalar functions, inlining, and performance: An entertaining title for a boring post - Adam Machanic
TSQL User-Defined Functions: Ten Questions You Were Too Shy To Ask - Robert Sheldon
Here is an inline table valued function version of your scalar function that uses the stuff() with select ... for xml path ('') method of string concatenation.:
create function dbo.fn_get_multiple_assigness_itvf (
#timelineid int
, #milstoneid int
, #taskid int
) returns table as return (
select Assignees = stuff((
select ',' + firstname + ' ' + lastname
from casetimelinepeople ctp
inner join userdetail ud
on ctp.peopleuserid=ud.userid
where casetimelineid = #timelineid
and templatemilestoneid = #milstoneid
and (templatetaskid = #taskid
or (#taskid = 0 and templatetaskid is null)
)
and ctp.isdeleted=0
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
)
go
rextester demo: http://rextester.com/UZTJS46485
test setup:
create table casetimelinepeople (
casetimelineid int
, peopleuserid int
, templatemilestoneid int
, templatetaskid int
, isdeleted bit not null default 0
);
insert into casetimelinepeople values
(1,1,1,null,0)
,(1,2,1,null,0)
,(1,3,1,null,0)
,(1,2,1,1,0)
,(1,3,1,1,0)
create table userdetail (
userid int not null
, firstname varchar(32) not null
, lastname varchar(32) not null);
insert into userdetail values
(1, 'Some', 'One')
,(2, 'Avinash', 'Raikwar')
,(3, 'Sql','Zim');
go
And querying the inline table valued function like so:
select *
from dbo.fn_get_multiple_assigness_itvf(1,1,0)
returns
+----------------------------------+
| Assignees |
+----------------------------------+
| Some One,Avinash Raikwar,Sql Zim |
+----------------------------------+
select *
from dbo.fn_get_multiple_assigness_itvf(1,1,1)
returns:
+-------------------------+
| Assignees |
+-------------------------+
| Avinash Raikwar,Sql Zim |
+-------------------------+
Using cross apply() to call the function for each row in a query:
select *
from casetimelinepeople ctp
cross apply dbo.fn_get_multiple_assigness_itvf(
ctp.casetimelineid
, ctp.templatemilestoneid
, ctp.templatetaskid
) x
returns:
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
| casetimelineid | peopleuserid | templatemilestoneid | templatetaskid | isdeleted | Assignees |
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
| 1 | 1 | 1 | NULL | False | Some One,Avinash Raikwar,Sql Zim |
| 1 | 2 | 1 | NULL | False | Some One,Avinash Raikwar,Sql Zim |
| 1 | 3 | 1 | NULL | False | Some One,Avinash Raikwar,Sql Zim |
| 1 | 2 | 1 | 1 | False | Avinash Raikwar,Sql Zim |
| 1 | 3 | 1 | 1 | False | Avinash Raikwar,Sql Zim |
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
I have a string value in db. I need to insert a , in between each 3 characters. Here is the example string.
Sample String
111100120125 // This sample is a dynamic string. So It can be less or more ..
Result
Val = 111,100,120,125
Please tell me about the builtin SqlServer function to get the , seperated string as a result.
Any help to this will be appreciated.
Thanks.
Please tell me about the builtin SqlServer function to get the ,
seperated string as a result.
You can use Stuff() function. Fiddle demo specific to your given string.
Declare #S varchar(50) = '111100120125'
SELECT STUFF(STUFF(STUFF(#S,4,0,','),8,0,','),12,0,',')
111,100,120,125
EDIT: More generic solution would be to create a function like below (Note that this function starts inserting separator value from end):
CREATE FUNCTION dbo.AddSeparator (#String VARCHAR(max), #Separator VARCHAR(1))
RETURNS VARCHAR(max)
AS
BEGIN
Declare #Length int = Len(#String)
WHILE (#Length > 3)
BEGIN
SELECT #String = STUFF(#String, #Length - 2, 0, #Separator),
#Length = #Length -3
END
RETURN #String
END;
Usage and fiddle demo :
SELECT String Original, dbo.AddSeparator(String, ',') Modified
FROM T
Results:
| ORIGINAL | MODIFIED |
|----------|-----------|
| | |
| 1 | 1 |
| 12 | 12 |
| 123 | 123 |
| 1234 | 1,234 |
| 12345 | 12,345 |
| 123456 | 123,456 |
| 1234567 | 1,234,567 |
You can use recursive CTE:
declare #f varchar(100)='111100120125'
;with pos as(
select 3 n, cast(SUBSTRING(#f,1,3) as varchar(max)) a
union all
select n+3 n, a+','+ SUBSTRING(#f,n+1,3) from pos
where n<LEN(#f)
)
select max(#f) ini, max(a) res from pos;
I'm hoping someone can help me. I'm trying to pivot some data on SQL Server 2005 and can't quite get the results I'm looking for.
This is my current table schema:
| ProductCode | AttributeName | AttributeValue |
| 1 | AttributeA | 10 |
| 1 | AttributeB | 20 |
| 2 | AttributeA | 30 |
| 2 | AttributeB | 40 |
| 3 | AttributeA | 50 |
This is the results I'm trying to achieve:
| ProductCode | AttributeA | AttributeB |
| 1 | 10 | 20 |
| 2 | 30 | 40 |
| 3 | 50 | NULL |
I know that I can achieve this result with the following SQL:
SELECT DISTINCT ProductCode,
(SELECT AttributeValue
FROM attributes
WHERE ProductName = 'AttributeA' AND ProductCode=a.ProductCode) AttributeA,
(SELECT AttributeValue
FROM attributes
WHERE ProductName = 'AttributeB' AND ProductCode=a.ProductCode) AttributeB,
FROM attributes a
Although that SQL does produce the result I'm looking for, it's obviously not dynamic (in reality, I not only have more Attribute Types, but different products have different sets of attributes) and it also scans the table 3 times. It's also a maintenance nightmare.
I tried using the PIVOT functionality of SQL Server, but with no luck.
Can anyone help?
create table #attributes (ProductCode int,
AttributeName varchar(20),
AttributeValue int)
insert into #attributes values (1, 'AttributeA', 10)
insert into #attributes values (1, 'AttributeB', 20)
insert into #attributes values (2, 'AttributeA', 30)
insert into #attributes values (2, 'AttributeB', 40)
insert into #attributes values (3, 'AttributeA', 50)
declare #attributes_columns nvarchar(max)
set #attributes_columns
= (
select ', [' + AttributeName + ']'
from
(
select distinct AttributeName as AttributeName
from #attributes
) t
order by t.AttributeName
for xml path('')
)
set #attributes_columns = stuff(#attributes_columns,1,2,'')
declare #sql nvarchar(max)
set #sql = N'
select ProductCode, <attributes_columns>
from
(select ProductCode, AttributeName, AttributeValue
from #attributes )p
pivot
(
sum(AttributeValue) for AttributeName in (<attributes_columns>)
) as pvt
'
set #sql = replace(#sql, '<attributes_columns>', #attributes_columns)
print #sql
exec sp_executesql #sql
drop table #attributes