how to remove duplicates from a comma seperated string in sql server - sql-server

how to remove duplicate values from the comma seperated string in sql server. Without using functions
Declare #data varchar(max) = '34.22,768.55,34.22,123.34,12,999.0,999.0'
My expected result should be
34.22,768.55,123.34,12,999.0
i tried this query but it doesn't remove duplicates from the variable.
Declare #data varchar(max) = '34.22,768.55,34.22,123.34,12,999.0,999.0'
set #data= (select '' + cast(cast('<d>'+replace(#data, ', ',',</d><d>')+'</d>' as xml).query('distinct-values(/d)') as varchar(max)) +'')

Please try this -
DECLARE #x AS XML=''
Declare #finalstring varchar(max) = ''
DECLARE #Param AS VARCHAR(100) = '34.22,768.55,34.22,123.34,12,999.0,999.0'
SET #x = CAST('<A>'+ REPLACE(#Param,',','</A><A>')+ '</A>' AS XML)
select #finalstring = #finalstring + value + ',' from (
SELECT t.value('.', 'VARCHAR(10)') Value FROM #x.nodes('/A') AS x(t))p
GROUP BY value
PRINT SUBSTRING(#finalstring,0,LEN(#finalstring))
OUTPUT
12,123.34,34.22,768.55,999.0
For sql 2016+
Declare #data varchar(max) = '34.22,768.55,34.22,123.34,12,999.0,999.0'
Declare #finalstring varchar(max) = ''
select #finalstring = #finalstring + value + ',' from string_split(#data,',')
GROUP BY value
PRINT SUBSTRING(#finalstring,0,LEN(#finalstring))
OUTPUT
12,123.34,34.22,768.55,999.0

Try this
Declare #data varchar(max) = '34.22,768.55,34.22,123.34,12,999.0,999.0'
SELECT STUFF(
(
SELECT DISTINCT ',' + UniqNum FROM
(
SELECT CAST('<d>'+replace(#data, ',','</d><d>')+'</d>' AS XML) AS numberXml
) as t1
CROSS APPLY
(
SELECT my_Data.D.value('.','varchar(50)') as UniqNum
FROM t1.numberXml.nodes('d') as my_Data(D)
) t2
FOR XML PATH('')
), 1, 1, '')
Result
UniqNumber
---------------------------
12,123.34,34.22,768.55,999.0

Try This
Declare #data varchar(max) = '34.22,768.55,34.22,123.34,12,999.0,999.0'
;WITH CTE
AS
(
SELECT
MyStr = SUBSTRING(#data,CHARINDEX(',',#Data)+1,LEN(#data)),
Val = SUBSTRING(#data,1,CHARINDEX(',',#data)-1)
UNION ALL
SELECT
MyStr = CASE WHEN CHARINDEX(',',MyStr)>0
THEN SUBSTRING(MyStr,CHARINDEX(',',MyStr)+1,LEN(MyStr))
ELSE NULL END,
Val = CASE WHEN CHARINDEX(',',MyStr)>0
THEN SUBSTRING(MyStr,1,CHARINDEX(',',MyStr)-1)
ELSE MyStr END
FROM CTE
WHERE ISNULL(REPLACE(MyStr,',',''),'')<>''
)
SELECT
Val = SUBSTRING(List,1,LEN(List)-1)
FROM
(
SELECT
DISTINCT Val+','
FROM CTE
WHERE ISNULL(MyStr ,'')<>''
FOR XML PATH('')
)Q(List)
My Result
12,123.34,34.22,768.55,999.0

Just an another simple way of doing it.
Declare #data Nvarchar(max) = N'34.22,768.55,34.22,123.34,12,999.0,999.0'
, #data2 Nvarchar(max)='';
SELECT #data = N'SELECT #DATA_DIST= #DATA_DIST+VAL+'',''
FROM (SELECT '''+replace(#data,',',''' AS VAL UNION SELECT ''')+''')A';
EXECUTE sp_executesql #data,N'#DATA_DIST varchar(MAX) OUTPUT',#DATA_DIST=#data2 OUTPUT;
SELECT LEFT(#data2,LEN(#data2)-1);
Result:
12,123.34,34.22,768.55,999.0

Related

How can I dynamically convert row to columns and have different column name for each column

How can I convert rows into columns and create different name for each column?
create table #TempTable (InvoiceNum int,State varchar(2), ChargeName varchar(50), PercentageRate decimal(5,3), FlatRate decimal(5,2))
insert into #TempTable values (235736, 'AZ','Inspection & Policy Fee', NULL,250.00)
,(235736, 'AZ','Surplus Line Tax',0.03,NULL)
,(235736, 'AZ','Stamping Fee',0.002,NULL
)
I need something like that:
UPDATE:
Using example I was able to unpivot it but the result is not what I wanted to:
create table #TempTable (InvoiceNum int,State varchar(2), ChargeName varchar(50), PercentageRate decimal(5,3), FlatRate decimal(5,2))
insert into #TempTable values (235736, 'AZ','Inspection & Policy Fee', NULL,250.00)
,(235736, 'AZ','Surplus Line Tax',0.03,NULL)
,(235736, 'AZ','Stamping Fee',0.002,NULL)
--select * from #TempTable
Declare #SQL nvarchar(max),
#query nvarchar(max)
select #SQL = STUFF((SELECT ',' + QUOTENAME(ChargeName)
from #TempTable
group by ChargeName, InvoiceNum
order by InvoiceNum
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--select #SQL
set #SQL = 'SELECT ' + #SQL + ' from
(
select PercentageRate, ChargeName
from #TempTable
) x
pivot
(
max(PercentageRate)
for ChargeName in (' + #SQL + ')
) p '
exec sp_executesql #SQL;
UPDATE:
Running below query gives me this:
Why ChargeName is not on the first row? I would expect to see it like this: What am I missing?
declare #TempTable table (InvoiceNum int,StateID varchar(2), ChargeName varchar(50), PercentageRate decimal(5,3), FlatRate decimal(5,2))
insert into #TempTable values (235736, 'AZ','Inspection & Policy Fee', NULL,250.00)
,(235736, 'AZ','Surplus Line Tax',0.03,NULL)
,(235736, 'AZ','Stamping Fee',0.002,NULL)
select
InvoiceNum,
ChargeName,
StateID,
PercentageRate,
FlatRate,
row_number() over (partition by InvoiceNum order by ChargeName) as RN
into #TempTable
from #TempTable #TempTable
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(RN)
FROM (SELECT DISTINCT RN FROM #TempTable) AS RN
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT InvoiceNum, ' + #ColumnName + '
FROM #TempTable
PIVOT(MAX(ChargeName)
FOR RN IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
drop table #TempTable
I would just join the temp table multiple times as needed.
Given your #TempTable
SELECT T1.InvoiceNum,
Tax1_Jurisdiction = T1.State, Tax1_TaxType = T1.ChargeName, Tax1_Percent = T1.PercentageRate, Tax1_FixedRate = T1.FlatRate,
Tax2_Jurisdiction = T2.State, Tax2_TaxType = T2.ChargeName, Tax2_Percent = T2.PercentageRate, Tax2_FixedRate = T2.FlatRate,
Tax3_Jurisdiction = T3.State, Tax3_TaxType = T3.ChargeName, Tax3_Percent = T3.PercentageRate, Tax3_FixedRate = T3.FlatRate
FROM #TempTable T1
JOIN #TempTable T2 ON T1.InvoiceNum = T2.InvoiceNum
JOIN #TempTable T3 ON T1.InvoiceNum = T3.InvoiceNum
WHERE T1.ChargeName = 'Inspection & Policy Fee'
AND T2.ChargeName = 'Surplus Line Tax'
AND T3.ChargeName = 'Stamping Fee'
;

Convert row data into column data using SQL Server

I would like to convert row data into columns, where the column names are not from the data. I think that using Pivot will not give me the correct solution. Please see Image of what my data looks like and how I want it to look.
The number of rows returned in the example will continue to grow over time.
My solution :
Based on #Triv answered, I have managed to solve the problem using the rank function to create a new column and then using dynamic pivot SQL to transform the data.
You could use a dynamic sql + PIVOT
CREATE TABLE #SampleData
(
AccountNumber int,
Product varchar(20),
ProductEndDate datetime
)
INSERT INTO #SampleData VALUES (1,'Fixed 10','2016-01-01'),(1,'Fixed 11','2016-02-01'),(1,'Fixed 13','2016-03-01'),(1,'Fixed 12','2016-04-01'),
(2,'Fixed 10','2016-01-01'),(2,'Fixed 11','2016-02-01'),(2,'Fixed 13','2016-03-01')
DECLARE #HeaderAll nvarchar(max)
DECLARE #ColumnPivotProduct nvarchar(max)
DECLARE #ColumnPivotProductEndDate nvarchar(max)
;WITH temp AS
(
SELECT DISTINCT
CONCAT('Product' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductGroup,
CONCAT('ProductEndDate' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductEndDateGroup
FROM #SampleData sd
)
SELECT #HeaderAll = STUFF((SELECT CONCAT(',',t.ProductGroup,'= MAX(',t.ProductGroup, '),', t.ProductEndDateGroup ,'= MAX(', t.ProductEndDateGroup,')') FROM temp t FOR XML PATH('')), 1,1,''),
#ColumnPivotProduct = STUFF((SELECT CONCAT(',',t.ProductGroup) FROM temp t FOR XML PATH('')), 1,1,''),
#ColumnPivotProductEndDate = STUFF((SELECT CONCAT(',', t.ProductEndDateGroup) FROM temp t FOR XML PATH('')), 1,1,'')
--SELECT #HeaderAll, #ColumnPivotProduct, #ColumnPivotProductEndDate
DECLARE #query nvarchar(max) = CONCAT(
';WITH temp AS
(
SELECT *,
CONCAT(''Product'' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductGroup,
CONCAT(''ProductEndDate'' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductEndDateGroup
FROM #SampleData sd
)
SELECT AccountNumber, ',#HeaderAll,' FROM
(
SELECT t.AccountNumber, t.Product, t.ProductEndDate, t.ProductGroup,t.ProductEndDateGroup FROM temp t
) src
PIVOT
(
MIN(Product) FOR ProductGroup IN (',#ColumnPivotProduct,')
) pvt
PIVOT
(
MIN(ProductEndDate) FOR ProductEndDateGroup IN (',#ColumnPivotProductEndDate,')
) pvt1
GROUP BY AccountNumber
')
PRINT #query
exec(#query)
DROP TABLE #SampleData
Demo link: http://rextester.com/AEQBZ56634
Note: CONCAT is avaiable in sql-server 2012+. IF you are using an older version, then use + for concatenating string
It works, check this out, Instead of #testTable use your table name
declare #temptable varchar(1000) = 'declare #tempTable1 table (',
#inserStatement varchar(1000) = 'insert into #tempTable1 (',
#insertValues varchar(1000) = ''
DECLARE #AcountNumber VARCHAR(50),#Product varchar(40),#ProductEndData varchar(50), #increment int = 0;
DECLARE db_cursor CURSOR FOR
select
Product,
ProductEndData from #testTable
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #Product, #ProductEndData
WHILE ##FETCH_STATUS = 0
BEGIN
set #increment = #increment+1;
SET #temptable += 'Product'+ cast(#increment as varchar) +' varchar(100),' + 'Product' + cast(#increment as varchar) + 'EndDate varchar(100),' ;
set #inserStatement += 'Product'+ cast(#increment as varchar) +',' + 'Product' + cast(#increment as varchar) + 'EndDate,';
set #insertValues += '(''' + #Product +''''+ ',' + ''''+ #ProductEndData + '''' + ')';
FETCH NEXT FROM db_cursor INTO #Product, #ProductEndData
END
CLOSE db_cursor
DEALLOCATE db_cursor
set #temptable = STUFF(#temptable, LEN(#temptable), 1, ')')
set #inserStatement = STUFF(#inserStatement, LEN(#inserStatement), 1, ')')
set #insertValues = replace(#insertValues, ')(', ',')
exec (#temptable + #inserStatement + ' values ' + #insertValues + 'select * from #tempTable1')

Showing "Invalid object name " in sqlServer?

While Executing the Following query it showing the Invalid object name '#temp1'. can any body knows the error occurred due to which reason this is my orginal code i used to fetch code , her differnt tables are formed i need to get the sum of the each row of each table
DECLARE #t TABLE (
id int IDENTITY(1,1),
BranchName nvarchar(max)
)
DECLARE #n int = 0,
#i int = 1,
#BranchName nvarchar(max),
#sql nvarchar(max),
#columns nvarchar(max)
INSERT INTO #t
SELECT DISTINCT BranchName
FROM ALX_Branches
SELECT #n = ##ROWCOUNT
WHILE #n >= #i
BEGIN
SELECT #BranchName = BranchName
FROM #t
WHERE id = #i
SELECT #columns = (
SELECT DISTINCT ','+QUOTENAME([SubInventory])
FROM #MyTempTable
WHERE [BranchName] = #BranchName
FOR XML PATH('')
)
SELECT #sql = N'--
SELECT * into #temp1
FROM (
SELECT [BranchID],
[SubInventory],
[Product],
[Stock]
FROM #MyTempTable
WHERE [BranchName] = ''' +#BranchName +'''
) as t
PIVOT (
MAX([Stock]) FOR [SubInventory] IN ('+STUFF(#columns,1,1,'')+')
) as pvt'
EXEC sp_executesql #sql
select * from #temp1
Firstly, there is no need for creating #temp1 table before.
because you are using "Select * into" that already create table within it.
Suppose type this note as a comment, but I don't have enough reputation score.
The reason of
Invalid object name '#temp1'
is: the variable #sql is NULL because #temp1 is not created yet via "Select * into" clause.
so append selecting from #temp1 within dynamic sql as the following:
SELECT #sql = N'--
SELECT * into #temp1
FROM (
SELECT [BranchID],
[SubInventory],
[Product],
[Stock]
FROM #MyTempTable
WHERE [BranchName] = ''' +#BranchName +'''
) as t
PIVOT (
MAX([Stock]) FOR [SubInventory] IN ('+STUFF(#columns,1,1,'')+')
) as pvt
select * from #temp1 '
EXEC sp_executesql #sql

How to compare two string elements in SQL Server

I have two strings in SQL Server.
For example:
declare #str1 as varchar(max)
declare #str2 as varchar(max)
set #str1 ='10:00am,2:00pm'
set #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
and I want to compare the two strings and want to get those elements string from #str2 which are not in #str1.
That means the result should be :
#str3 = '12:00pm,4:00pm,6:00pm,8:00pm'
Try this :-
declare #str1 as varchar(max)
declare #str2 as varchar(max)
set #str1 ='10:00am,2:00pm'
set #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
--the below 2 CTE's are used for splitting the string into different rows
;with cteStr1(str1) as
(
SELECT
RIGHT(LEFT(#str1,Number-1),
CHARINDEX(',',REVERSE(LEFT(','+#str1,Number-1)))) as str1
FROM
master..spt_values
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(#str1)+1
AND
(SUBSTRING(#str1,Number,1) = ',' OR SUBSTRING(#str1,Number,1) = '')
),cteStr2(str2) as
(
SELECT
RIGHT(LEFT(#str2,Number-1),
CHARINDEX(',',REVERSE(LEFT(','+#str2,Number-1)))) as str2
FROM
master..spt_values
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(#str2)+1
AND
(SUBSTRING(#str2,Number,1) = ',' OR SUBSTRING(#str2,Number,1) = '')
)
Select str2 from cteStr2
except
select str1 from cteStr1
Try this
DECLARE #str1 VARCHAR(MAX)
DECLARE #str2 VARCHAR(MAX)
SET #str1 ='10:00am,2:00pm'
SET #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
SET #str1 = ',' + #str1 + ','
SET #str2 = ',' + #str2 + ','
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #str1) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #str1)
SELECT #name = SUBSTRING(#str1, 1, #pos-1)
SELECT #str1 = SUBSTRING(#str1, #pos+1, LEN(#str1)-#pos)
IF #name <> ''
BEGIN
SET #str2 = REPLACE(#str2,','+#name,'')
END
END
SET #str2 = REPLACE(#str2,','+#name,'')
SELECT SUBSTRING(#str2, 2, LEN(#str2)-2) AS Result
Try this one -
DECLARE
#str1 VARCHAR(500)
, #str2 VARCHAR(500)
SELECT
#str1 = '10:00am,2:00pm'
, #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
;WITH cte AS
(
SELECT
id = p.value('(./n)[1]', 'INT')
, tm = p.value('(./s)[1]', 'VARCHAR(500)')
FROM (
SELECT field = CAST('<r><s>' + REPLACE(SUBSTRING(t.string + ',', 1, LEN(t.string + ',')), ',', '</s><n>' + CAST(t.id AS VARCHAR(10)) + '</n></r><r><s>') + '</s></r>' AS XML)
FROM (
SELECT string = #str1, id = 1
UNION ALL
SELECT #str2, 2
) t
) d
CROSS APPLY field.nodes('/r') t(p)
WHERE t.p.exist('n') = 1
)
SELECT tm FROM cte WHERE id = 2
EXCEPT
SELECT tm FROM cte WHERE id = 1
Or try this -
;WITH cte2 AS
(
SELECT
t.id
, tm =
SUBSTRING(
',' + t.string + ','
, number + 1
, CHARINDEX(',', ',' + t.string + ',', number + 1) - number - 1)
FROM (
SELECT string = #str1, id = 1
UNION ALL
SELECT #str2, 2
) t
CROSS JOIN [master].dbo.spt_values n
WHERE [type] = 'p'
AND number <= LEN(',' + t.string + ',') - 1
AND SUBSTRING(',' + t.string + ',', number, 1) = ','
)
SELECT tm FROM cte2 WHERE id = 2
EXCEPT
SELECT tm FROM cte2 WHERE id = 1

How to return a string in reverse separated by a special character in SQL?

I have a string like this "Planck, Albert, Bohr"
I want the output as "Bohr Albert Planck"
I want this done using SQL server. I saw many string split functions all return the data as a table and in the forward order. These don't server my purpose.
Any pointers will be helpful.
This what you want:
DECLARE #source VARCHAR(MAX)
DECLARE #dest VARCHAR(MAX)
DECLARE #lenght INT
SET #source = 'Planck, Albert, Bohr'
SET #dest = ''
WHILE LEN(#source) > 0
BEGIN
IF CHARINDEX(' ', #source) > 0
BEGIN
SET #dest = SUBSTRING(#source,0,CHARINDEX(' ', #source)) + ' ' + #dest
SET #source = LTRIM(RTRIM(SUBSTRING(#source,CHARINDEX(' ',
#source)+1,LEN(#source))))
END
ELSE
BEGIN
SET #dest = #source + ' ' + #dest
SET #source = ''
END
END
SELECT REPLACE(REPLACE(#dest,Char(44),''),Char(13), '')
Char(44) is the ASCII value for , so at last I am just replacing that character.
This will print Bohr Albert Planck.
If you use a split string function like this you can do something like this.
declare #S varchar(20) = 'Planck, Albert, Bohr'
select
(
select ltrim(s)+' '
from dbo.Split(',', #S)
order by pn desc
for xml path(''), type
).value('.', 'varchar(max)')
SQL Fiddle
Declare #inStr varchar(1000)='Planck, Albert, Bohr'
;WITH CTE AS(
select ltrim(rtrim(reverse(left(reverse(#instr),CHARINDEX(',',reverse(#instr),1)-1)))) as strg,RIGHT(reverse(#instr),LEN(reverse(#instr))-CHARINDEX(',',reverse(#instr),1)) as rem
union all
select CASE WHEN CHARINDEX(',',c.rem,1)>0 then ltrim(rtrim(reverse(left(rem,CHARINDEX(',',rem,1)-1)))) else reverse(rem) end,
CASE WHEN CHARINDEX(',',c.rem,1)>0 then RIGHT(c.rem,LEN(rem)-CHARINDEX(',',rem,1)) else '' end
from CTE c
where len(rem) > 0 --CHARINDEX(',',c.rem,1)>0
)
select stuff((select ' '+strg from CTE for xml path('')),1,1,'')

Resources