Remove duplicates from String using sql - sql-server

If I have a value something like '10,10,20,30,40,20' in a field of table, then I want to make it as '10,20,30,40'
Is there any sql function to do such thing?
Thanks
Sudhakar

using Jeff's DelimitedSplit8K from http://www.sqlservercentral.com/articles/Tally+Table/72993/
declare #value varchar(100) = '10,10,20,30,40,20',
#new_value varchar(100)
select #new_value = isnull(#new_value + ',', '') + Item
from DelimitedSplit8K(#value, ',')
group by Item
order by Item
select #new_value

Did this long ago. This might need some modifications. But it generates output.
Try :
DECLARE #Data_String AS VARCHAR(1000), #Result as varchar(1000)=''
SET #Data_String = '10,10,20,30,40,20'
SET #Data_String = REPLACE(#Data_String,'|',',')
select #Data_String;
SELECT #Result=#Result+col+',' from(
SELECT DISTINCT t.c.value('.','varchar(100)') col from(
SELECT cast('<A>'+replace(#Data_String,',','</A><A>')+'</A>' as xml)col1)data
cross apply col1.nodes('/A') as t(c))Data
SELECT LEFT(#Result,LEN(#Result)-1)

believing it stores integer number you can get them with creating a function first you need to split the values then have to use a distinct function as below
1st create a function like
CREATE FUNCTION [dbo].[idpGetSplitedString]
(
#String varchar(8000),
#Delimiter char(1)
)
RETURNS
#temptable TABLE
(
items varchar(8000)
)
AS
BEGIN
declare #idx int
declare #slice varchar(8000)
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) values(rtrim(ltrim(#slice)))
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
RETURN
END
then call the function like
select [dbo].idpGetSplitedString as Values

Related

TSQL: multiple split string in one query

I have a string_split function which looks like below
CREATE FUNCTION [dbo].[String_Split](#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE ([value] varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
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([value]) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
I am now successfully able to split the string using the below code
declare #NameList nvarchar(100) = 'Hi,Hi1,Hi2';
SELECT * FROM string_split(#NameList,',')
but now I want to split multiple string using the same function like below
declare #NameList nvarchar(100) = 'Hi,Hi1,Hi2';
declare #DESCLIST nvarchar(100) = 'Hii,Hii1,Hii2';
I want to split these strings in different columns,
my expected output is
col 1 col 2
------------------
Hi |Hii
Hi1 |Hii1
Hi2 |Hii2
how can I achieve this?
You can join them on row-number.
SELECT
s1.value,
s2.value
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM STRING_SPLIT(#NameList, ',') s
) s1
JOIN (
SELECT *,
rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM STRING_SPLIT(#DESCLIST, ',') s
) s2 ON s2.rn = s1.rn;
db<>fiddle
I strongly suggest you store multiple items in separate rows, rather than a comma-separated list. Consider using a table variable, temporary table or Table Valued Parameter.

How to get split string with quotation on each split item in SQL Server?

How to get split string with quotation on each split item in SQL Server? I have tried this
declare #departmentNames as varchar(max) = 'Account, hod'
--declare #departmentNames as varchar(max) = 'Account'+', '+'hod'
print #departmentNames
I get result like this => Account,hod
but I want it like this => 'Account', 'hod'
so that I could use it in
select *
from tblDepartment
where name in (select item from splitString(#departmentNames, ','))
I know if I use integers with id column it will work fine i.e => 1, 2, 3, 4 but I want to try it with strings.
So is there anyone who can help me with this?
You can use apply :
select td.*
from tblDepartment td cross apply
<shema>.splitString(#departmentNames, ',') spt(item) -- add schema name
where spt.item = td.name;
If you want string comparison, you can do concatenation.
Note : use Schema name while calling UDF function.
First create this function:
CREATE FUNCTION [dbo].[fn_Split]
(#String varchar(8000),
#Delimiter varchar(50))
RETURNS #temptable TABLE (items varchar(8000))
AS
BEGIN
/*
SELECT * FROM dbo.fn_Split('12345;thome', ';')
*/
DECLARE #idx int
DECLARE #slice varchar(8000)
DECLARE #delimiterLength int
SET #delimiterLength = len(#Delimiter)
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)
VALUES (LTRIM(RTRIM(#slice)))
SET #String = RIGHT(#String, LEN(#String) - #idx - #delimiterLength + 1)
IF LEN (#String) = 0
BREAK
END
RETURN
END
After creating this function then you can test with below query.
It splits words with any delimiter you are passing
select items from dbo.fn_Split('ACCOUNT ,HOD',',')
select items from dbo.fn_Split('ACCOUNT ; HOD',';')
Then pass variable and and use join with this function.
Use table alias for easy understanding
declare #departmentNames as varchar(max) = ('Account, hod')
select t.*
from tblDepartment t
inner join
(Select items
from dbo.fn_Split (#departmentNames, ',')) A on t.name = A.items
I create temptable for testing and this query will return output like below

Optimize finding the Nth occurrence of character in string

I wrote a sql server function which returns substring before the Nth occurence of character.
For example,
SELECT dbo.fn_getFirstNthSentence('.', 'hello world.It.is.raining.today', 3)
returns 'hello world.It.Is.' as a result.
The function I wrote looks dirty and slow so I want to optimize it.
Any advice to make it clean is appreciated.
Thank you.
CREATE FUNCTION fn_getFirstNthSentence
(
#TargetStr VARCHAR(MAX) ,
#SearchedStr VARCHAR(8000) ,
#Occurrence INT
)
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #pos INT ,
#counter INT ,
#ret INT;
SET #pos = CHARINDEX(#TargetStr, #SearchedStr);
IF ( #pos = 0 )
RETURN #SearchedStr
SET #counter = 1;
IF #Occurrence = 1
SET #ret = #pos;
ELSE
BEGIN
WHILE ( #counter < #Occurrence )
BEGIN
IF(LEN(#SearchedStr) < #pos + 1)
RETURN #SearchedStr
SELECT #ret = CHARINDEX(#TargetStr, #SearchedStr,
#pos + 1);
IF(#ret = 0)
RETURN #SearchedStr
SET #counter = #counter + 1;
SET #pos = #ret;
END;
END;
RETURN LEFT(#SearchedStr, #ret)
END;
Here is yet another option using a delimited string splitter. The XML method already posted is a good one but this approach does not require a table variable.
This is created as an inline table valued function which should keep the performance really fast.
create function fn_getFirstNthSentence
(
#SearchedStr varchar(100)
, #Occurrence int
, #Delimiter char(1)
) returns table as return
with ParsedValues as
(
select Item
, ItemNumber
from dbo.DelimitedSplit8K(#SearchedStr, #Delimiter)
where ItemNumber <= #Occurrence
)
select top 1 ResultString = STUFF(
(
select #Delimiter + Item
from ParsedValues
order by ItemNumber
for xml path('')), 1,1, '') + #Delimiter
from ParsedValues
This is also using a splitter created by Jeff Moden. It has one feature that none of the other splitter have...a column to indicate which position the value came from. You can find his article an ensuing discussion here. http://www.sqlservercentral.com/articles/Tally+Table/72993/
Then if you want to execute it you can do this quite simply.
declare #String varchar(100) = 'hello world.It.is.raining.today.'
, #Num int = 3
, #Delimiter char(1) = '.'
;
select *
from fn_getFirstNthSentence(#String, #Num, #Delimiter)
If you don't like Jeff Moden's splitter you can find several other options here. http://sqlperformance.com/2012/07/t-sql-queries/split-strings I don't use Moden's for everything but when you need to keep the parsed values in order it is awesome.
--EDIT--
Here is how you could modify this to become a scalar function instead of an inline table valued function. My preference would be to keep the itvf as they are faster and more flexible.
create function fn_getFirstNthSentenceScalar
(
#SearchedStr varchar(100) = 'hello world.It.is.raining.today.this is after 5'
, #Occurrence int = 5
, #Delimiter char(1) = '.'
) returns varchar(max) as begin
declare #RetVal varchar(max);
with ParsedValues as
(
select Item
, ItemNumber
from dbo.DelimitedSplit8K(#SearchedStr, #Delimiter)
where ItemNumber <= #Occurrence
)
select top 1 #RetVal = STUFF(
(
select #Delimiter + Item
from ParsedValues
order by ItemNumber
for xml path('')), 1,1, '') + #Delimiter
from ParsedValues;
return #RetVal
end
--I find these functions to be a mine-field, and at the risk of stepping on a mine I've tried some simplifications - maybe a microscopic improvement in performance
alter FUNCTION fn_getFirstNthSentence
(
#TargetStr VARCHAR(MAX) ,
#SearchedStr VARCHAR(8000) ,
#Occurrence INT
)
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #pos INT ,
#counter INT ;
IF #Occurrence < 1
RETURN NULL;
SELECT #counter = 0, #POS = 1;
WHILE (#counter < #Occurrence AND #POS > 0)
BEGIN
SELECT #POS = CHARINDEX(#TargetStr, #SearchedStr,
#pos + 1);
IF #POS > 0
SET #counter = #counter + 1;
END;
RETURN CASE WHEN #POS > 0 THEN
LEFT(#SearchedStr, #POS)
ELSE
#SearchedStr
END;
END;
Another option is via XML
I can't see your benchmarks, but it is certainly far less code. An added option could be Find the 3rd through 5th occurrence by adding a parameter and changing the Where Seq<=#FindPos to Where Seq Between range1 and range2.
Declare #FindPos int = 3
Declare #String varchar(max) = 'hello world.It.is.raining.today'
Declare #Delim varchar(10) = '.'
Declare #XML xml,#RetVal varchar(max) = ''
Set #XML = Cast('<x>' + Replace(#String,#Delim,'</x><x>')+'</x>' as XML)
Declare #Table table (Seq int identity(1,1),String varchar(max))
Insert Into #Table Select ltrim(rtrim(String.value('.', 'varchar(max)')))+#Delim as value FROM #XML.nodes('x') as T(String)
Select #RetVal=#RetVal + String from #Table Where Seq<=#FindPos Order By Seq
Select #RetVal
Returns
hello world.It.is.
EDIT: If it helps, below is my generic parsing function which returns a
normalized table...
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
-- Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
-- Select * from [dbo].[udf-Str-Parse]('id26,id46|id658,id967','|')
-- Select * from [dbo].[udf-Str-Parse]('hello world. It. is. . raining.today','.')
Returns #ReturnTable Table (Key_PS int IDENTITY(1,1), Key_Value varchar(max))
As
Begin
Declare #XML xml;Set #XML = Cast('<x>' + Replace(#String,#Delimeter,'</x><x>')+'</x>' as XML)
Insert Into #ReturnTable Select Key_Value = ltrim(rtrim(String.value('.', 'varchar(max)'))) FROM #XML.nodes('x') as T(String)
Return
End
So for example:
Select * from [dbo].[udf-Str-Parse]('hello world.It.is.raining.today','.')
Returns
Key_PS Key_Value
1 hello world
2 It
3 is
4 raining
5 today

T-SQL How can I use a variable in the WHERE IN clause? [duplicate]

This question already has answers here:
Parameterize an SQL IN clause
(41 answers)
Closed 7 years ago.
I can do this and it works (but would like something more simple):
Declare #PropIDs varchar(50)
Set #PropIDs = '1, 2'
IF OBJECT_ID('dbo.TempProp') IS NOT NULL DROP TABLE dbo.TempProp
CREATE TABLE [dbo].[TempProp](
[PropCode] [VarChar](3) NULL)
Set #Sql = 'Insert Into TempProp Select PropertyCode From Property where PropertyID In (' + #PropIDs + ')'
Execute (#Sql)
But I wish I could do just this:
Declare #PropIDs
Set #PropIDs = '1, 2'
Select PropertyCode
Into #TempProp
From Property where PropertyID IN (#PropIDs)
It is the "...Property IN (#PropIDs)" that is giving me trouble.
Any suggestions?
Create a split table value function similar to this one
create FUNCTION [dbo].[Split](#String varchar(MAX), #Delimiter char(1))
returns #temptable TABLE (items varchar(MAX))
as
begin
declare #idx int
declare #slice varchar(8000)
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) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end;
That code is from this question: separate comma separated values and store in table in sql server
Then use it in your query like this:
Declare #PropIDs
Set #PropIDs = '1, 2'
Select PropertyCode
Into #TempProp
From Property where PropertyID IN (dbo.Slit(#PropIDs, ','))

How to use the SQL SPLIT function?

I wish to use the SQL "split" function:
alter FUNCTION [dbo].[Split3] (#String nvarchar(1000), #Delimiter char(1))
returns #temptable TABLE (items nvarchar(1000))
as
begin
declare #idx int
declare #slice nvarchar(1000)
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) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
Select * from dbo.Split3 ((Select eqipproc from equipmast where eqcode = 'EQL0000004'),';')
ERROR
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near '('.
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near ','.
you have to pass CSV string to your split function
DECLARE #result nvarchar(max)
SET #result = ''
SELECT #result = #result + [eqipproc ] + N';'
equipmast where eqcode = 'EQL0000004'
now pass #result to your split function
Select * from dbo.Split3(#result,';')
Try this function
CREATE Function dbo.Str_Split(#string varchar(100),#dl varchar(2))
Returns #outputtbl Table(col varchar(5))
As
BEGIN
Declare #remainingStr varchar(100)=#string
if(CHARINDEX(#dl,#remainingStr,1) = 0)
begin
INSERT INTO #outputtbl
select #remainingStr
end
else
begin
While(CHARINDEX(#dl,#remainingStr,1) > 0)
BEGIN
INSERT INTO #outputtbl
select LEFT(#remainingStr,CHARINDEX(#dl,#remainingStr,1)-1)
SET #remainingStr=RIGHT(#remainingStr,LEN(#remainingStr)-CHARINDEX(#dl,#remainingStr,1))
end
INSERT INTO #outputtbl
select #remainingStr
END
Return
END
--select * from dbo.Str_Split('ab,cd,efg',',')
No matter if filtering by eqcode = 'EQL0000004' returns only one row or more than one, you can execute your [dbo].[Split3] function for each returned row by using CROSS APPLY:
select s.*
from equipmast as e
cross apply dbo.Split3(e.eqipproc , ';') as s
where e.eqcode = 'EQL0000004'
Note: the above solution with CROSS APPLY does the same like the following (which is similar to Utkarsh's answer):
declare #s nvarchar(1000)
select #s = eqipproc from equipmast where eqcode = 'EQL0000004'
select * from dbo.Split3(#s, '.')
The difference is that CROSS APPLY also works if the query returns more than one row.

Resources