Split a string with 2 delimiters in SQL Server - sql

I have some wrong data in my SQL Server database.
It's look like this:
abc.qwerty#yahoo.com|fubar#cc.uk|helloworld#gmail.com
or
abc.qwerty#yahoo.com;fubar#cc.uk
How can I split them to separate email if the delimiter can be anyone of these '|', ';', ','.
After spliting, the result will be add to 3 column in the same table:
Email1: abc.qwerty#yahoo.com, Email2: fubar#cc.uk, Email3: helloworld#gmail.com

DECLARE #STR NVARCHAR(MAX)='abc.es#yahoo.com|shudo#cc.uk|maria#gmail.com, abc.es#yahoo.com;shudo#cc.uk'
-- Converts values to rows
SELECT Split.a.value('.', 'VARCHAR(100)') 'Ids'
FROM
(
-- Use 3 REPLACE for '|', ';', ','
SELECT CAST ('<M>' + REPLACE(REPLACE(REPLACE(#STR, '|', '</M><M>'),',','</M><M>'),';','</M><M>') + '</M>' AS XML) AS Data
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
See the working FIDDLE here
UPDATE
If you want Comma Separated Value as final string you can do the below
DECLARE #STR NVARCHAR(MAX)='abc.qwerty#yahoo.com|fubar#cc.uk|helloworld#gmail.com,abc.qwerty#yahoo.com;fubar#cc.uk '
DECLARE #Final VARCHAR(MAX)='';
-- Converts values to rows
;WITH CTE AS
(
SELECT DISTINCT
Split.a.value('.', 'VARCHAR(100)') 'Ids'
FROM
(
-- Use 3 REPLACE for '|', ';', ','
SELECT CAST ('<M>' + REPLACE(REPLACE(REPLACE(#STR, '|', '</M><M>'),',','</M><M>'),';','</M><M>') + '</M>' AS XML) AS Data
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
)
-- Convert to Comma Seperated Values
SELECT #Final +=
Isnull('Email' + CAST(ROW_NUMBER() OVER(ORDER BY IDS)AS VARCHAR(10)) + ': ' + Ids, '') + ', '
FROM CTE
SELECT LEFT(#Final,len(#Final)-1)
PRINT #Final
UPDATE 2 : Split delimiter separated values to rows and convert to columns dynamically
I have written logic inside query
DECLARE #STR NVARCHAR(MAX)='abc.es#yahoo.com|shudo#cc.uk|maria#gmail.com,abc.es#yahoo.com;shudo#cc.uk'
;WITH CTE AS
(
-- Converts values to rows
SELECT DISTINCT Split.a.value('.', 'VARCHAR(100)') 'Ids'
FROM
(
-- Use 3 REPLACE for '|', ';', ','
SELECT CAST ('<M>' + REPLACE(REPLACE(REPLACE(#STR, '|', '</M><M>'),',','</M><M>'),';','</M><M>') + '</M>' AS XML) AS Data
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
)
-- Create a column to order Emails like Email1,Email2.... and insert to a temp table
-- It can be done without temp table, but its for sake of readability
SELECT 'Email' + CAST(ROW_NUMBER() OVER(ORDER BY Ids)AS VARCHAR(10)) EMails,Ids
INTO #TEMP
FROM CTE
-- Get columns to pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + EMails + ']','[' + EMails + ']')
FROM (SELECT DISTINCT EMails FROM #TEMP) PV
ORDER BY EMails
-- Pivot the result(convert to columns from rows)
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
SELECT EMails,Ids
FROM #TEMP
) x
PIVOT
(
MIN(Ids)
FOR EMails IN (' + #cols + ')
) p;'
EXEC SP_EXECUTESQL #query
SQL FIDDLE
Now if you want to insert emails into your table you have created, you can use the below code
DECLARE #query NVARCHAR(MAX)
SET #query = 'INSERT INTO YOURTABLE(' + #cols + ')
SELECT * FROM
(
SELECT EMails,Ids
FROM #TEMP
) x
PIVOT
(
MIN(Ids)
FOR EMails IN (' + #cols + ')
) p;'
EXEC SP_EXECUTESQL #query

Step one I would clean your data. SQL Server has a Replace function you can use.
REPLACE ( string_expression , string_pattern , string_replacement )
Example you can run in SQL Server Management Studio:
select Replace(Replace('ab|de,ghi;de', ',', '|'), ';', '|')
Would first replace the commas, and then the semicolons. Resulting in ab|de|ghi|de
Alternatively, you could just split up the data multiple times by running a cursor on the resulting split (with whatever mechanism you are using to split.

Related

Select not null columns as a comma separated string from dynamic sql

I am trying to do my best to avoid using cursors. There is a comma separated list of columns in nvarchar variable that looks like this:
#columnList = 'col1,col2,col5'
There is a table with lots of varchar columns:
myTable: [col1],[col2],[col3],[col4],[col5],[col6],[col7]
This is how I select data using a dynamic sql:
exec ('select ' + #columnList + ' from myTable')
This query returns the following results:
[col1], [col2] , [col5]
null , "txt1" , null
"txt2", null , null
null , "txt3" , "txt4"
This is what I need to get:
#resultList = "txt1,txt2,txt3,txt4"
How do I select a comma separated string containing not-null values only? I know how to convert a table to comma separated string, so getting something like:
[column]
"txt1"
"txt2"
"txt3"
"txt4"
Is also fine. Any suggestions? Feel free to suggest a basic approach, I don't expect you to write the actual code for me.
You can use a solution like the following, using just a REPLACE to create the SQL query:
DECLARE #columnList VARCHAR(100)
SET #columnList = 'col1,col2,col5'
SET #columnList = REPLACE(#columnList, ',', ' AS colName FROM myTable UNION ALL SELECT ');
EXEC('SELECT * FROM (SELECT ' + #columnList + ' AS colName FROM myTable)t WHERE NOT t.colName IS NULL');
You can also use a solution using UNPIVOT:
DECLARE #columnList VARCHAR(100);
SET #columnList = 'col1,col2,col5';
EXEC('SELECT colName FROM (SELECT ' + #columnList + ' FROM myTable) t1 UNPIVOT (colName FOR columnNames IN (' + #columnList + ')) AS t2');
demo on dbfiddle.uk
Since you mention you already know how to aggregate into comma-seperated, here's how to unpivot your table with cross apply:
select unpivoted.*
from myTable
cross apply
( values
('col1',col2)
,('col2',col2)
,('col3',col3) -- etc
)unpivoted(colname,colval)
This would work for SQL Server 2017, on earlier version STRING_AGG usage should be replaced with XML-based solution.
You could first concatenate your results into a single line and then use STRING_AGG to aggregate them into a single string:
;with t as
(
SELECT * FROM (VALUES
(null , 'txt1' , null),
('txt2', null , null),
(null , 'txt3' , 'txt4')) x(col1, col2, col5)
)
SELECT STRING_AGG(STUFF(CONCAT(',' + col1, ',' + col2, ',' + col5), 1, 1, ''), ',')
FROM t
CTE is just for showcasing, you can simply do it in your dynamic query:
DECLARE #columnList NVARCHAR(MAX) = 'col1,col2,col5'
DECLARE #query NVARCHAR(MAX) = ''
SELECT #query = 'SELECT STRING_AGG(STUFF(CONCAT('','' + ' + REPLACE(#columnList, ',', ', '','' + ') + '), 1, 1, ''''), '','') from mytable'
EXEC sp_executesql #query
Working example on dbfiddle

Return SELECT query result as a CSV string

I have the following Sql Server 2016 SELECT statement that returns only 1 row:
SELECT TOP 1 * FROM tempdb.dbo.IMTD
How can I concatenate the values as a comma delimited string? NOTE: the column names of this temporary table are unknown as they can variate.
Thank you.
Something like this perhaps:
-- Sample data
DECLARE #someTable TABLE (SomeID int identity, SomeTxt varchar(100));
INSERT #someTable VALUES ('row1'),('row2'),('row3');
-- Solution
SELECT ConcatinatedString =
STUFF
((
SELECT ','+SomeTxt
FROM #someTable
FOR XML PATH(''), TYPE
).value('.','varchar(100)'),1,1,'');
You can use Dynamic query as below:
DECLARE #COLS VARCHAR(MAX) = ''
SELECT #COLS = #COLS + ',' + COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '#table[_]%' -- Dynamic Table (here, Temporary table)
DECLARE #COLNAMES VARCHAR(MAX) = REPLACE(STUFF(#COLS, 1, 1, ''), ',', '+ '','' +')
Declare #cmd varchar(max) = 'Select ' + #COLNAMES + ' as CSVCol from #table'
-- will generate
-- Select Column1+ ',' +Column2+ ',' +Column3 as CSVCol from #table
EXEC (#cmd)
Another solution you can try is this.
SELECT LTRIM(RTRIM(<ColumnName1>)) + ',',
LTRIM(RTRIM(<ColumnName2>)) + ',',
...
LTRIM(RTRIM(<ColumnNamen>)) + ','
FROM tempdb.dbo.IMTD
If you only want one row keep that top 1 In there like
SELECT TOP 1
LTRIM(RTRIM(<ColumnName1>)) + ',',
LTRIM(RTRIM(<ColumnName2>)) + ',',
...
LTRIM(RTRIM(<ColumnNamen>)) + ','
FROM tempdb.dbo.IMTD
The LTRIM and RTRIM will remove any white space and this should allow you to copy and paste the result set anywhere you may need it. You will need to do this for each columnname.
You can use the query below to get the column names from your temp table.
DECLARE #ColumnNames NVARCHAR(MAX)
SELECT
#ColumnNames= COALESCE(#ColumnNames +',','')+COLUMN_NAME
FROM
TempDB.INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '#TempTableName'

How to get all records on the basis of Search String In sql

I want to fetch all the records on the basis of Search string
E.G.
Column name: FileName
MasterRoomTwo.jpg
BedRoom.png
MasterbedRoom.gif
and in simple scenario I can use
Declare #FileName nvarchar(60) = NULL
set #FileName = '.jpg'
SELECT *
FROM JobAttachment
WHERE AND Tags LIKE '%' + ISNULL(#FileName ,FileName ) + '%'
ORDER BY updated DESC
but in my case I will get like
set #FileName = '.jpg,.Png,gif'
So how to make query like this?
Any help will be appreciated.
Thanks
Try this. Split the input string and use charindex
SELECT 'MasterRoomTwo.jpg' a INTO #temp UNION
SELECT 'BedRoom.png' UNION
SELECT 'MasterbedRoom.gif'
DECLARE #FileName NVARCHAR(60)
SET #FileName = '.jpg,.Png,gif'
SELECT *
FROM #temp
JOIN (SELECT Rtrim(Ltrim(Split.a.value('.', 'VARCHAR(100)'))) fs
FROM (SELECT Cast ('<M>' + Replace(#FileName, ',', '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)) ad
on Charindex(fs, a) > 0
Try this :
SELECT *
FROM JobAttachment a
JOIN (SELECT t1.nod.value('.', 'varchar(50)') tags
FROM (SELECT Cast('<N>.'
+ Replace(Replace(#FileName, '.', ''), ',', '</N><N>.')
+ '</N>' AS XML) AS format) t
CROSS APPLY format.nodes('/N') AS t1(nod)
WHERE t1.nod.value('.', 'varchar(50)') <> '.') fileformat
ON a.tag LIKE ( '%' + fileformat.tags + '%' )
You can create dynamic condition as
Declare #FileName nvarchar(60) = NULL
set #FileName = '.jpg,.Png,gif'
--append a comma to the string to get correct results with empty strings
--or strings with a single value (no commas)
SET #FileName = #FileName + ',';
declare #x XML
declare #FileSearch nvarchar(max)
select #x = cast( '<F>' + replace ( #FileName,',','</F><F>') + '</F>' as xml)
select #FileSearch = stuff( isnull(#FileSearch , '') +
' OR FileName Like ''%'+ isnull(t.value('.','nvarchar(60)'),'')
+'%''' ,1,3,'')
from #x.nodes('/F') as x(t)
And then create dynamic query to get desired results:
set #sql = 'select * from test where ' -- entire query goes here
+ #FileSearch
exec sp_executesql #sql
DEMO

Comma separated column value from Temporary Table

I am inserting select statement result to Temporary table. I want its one column value comma separated, so I tried following but it is not working.
SELECT #IdList = COALESCE(#IdList+',' ,'') + s.Id
FROM (SELECT Id FROM #_TempStudentTable) as s
I have checked data in #_TempStudentTable, it is having data. But when i try to display IdList, it returns NULL.
Help me. I don't know what's the problem.
Try this :
DECLARE #IdList VARCHAR(MAX)
SELECT #IdList = COALESCE(#IdList + ',','') + CAST(Id AS VARCHAR(100))
FROM #_TempStudentTable
SELECT #IdList
You can get it without variable also using XML like below
SELECT STUFF(( SELECT ',' + CAST(Id AS VARCHAR(10))
FROM #_TempStudentTable
FOR XML PATH (''))
, 1, 1, '')
In SQL Server, you may use the below query to get a list of comma separated column names/headers:
SELECT STUFF((
SELECT ',' + CAST(name AS VARCHAR(50))
FROM (
SELECT name
FROM tempdb.sys.columns
WHERE [object_id] = OBJECT_ID(N'tempdb..#temptablename')
) k
FOR XML PATH('')
), 1, 1, '')

how to split and concatenate in sql server?

I am passing in a string into a stored procedure to be used in a select statement using dynamic sql:
#groups as nvarchar(1000) = 'group1,group10,group8'
I might just pass in string of numbers, eg, '1,2,3,4'
I want to split these values and then concatenate them so that they end up in the following manner :
'rmc.group1,rmc.group10,rmc.group8'
declare #groups nvarchar(1000) ='group1,group10,group8'
set #groups = 'rmc.' + replace(#groups, ',', ',rmc.')
select #groups
Result:
rmc.group1,rmc.group10,rmc.group8
Sql Fiddle Demo
Select Replace('group1,group10,group8','group','rmc.group')
Try this one -
DECLARE #groups nvarchar(1000) = 'group1,group10,group8'
SELECT STUFF((
SELECT ',rmc.' + t
FROM (
SELECT t = t.c.value('.', 'VARCHAR(50)')
FROM (
SELECT ID = CAST ('<t>' + REPLACE(#groups, ',', '</t><t>') + '</t>' AS XML)
) r
CROSS APPLY ID.nodes ('/t') t(c)
) t
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1,1, '')
Output -
------------------------------------
rmc.group1,rmc.group10,rmc.group8