Return comma-separated list from SQL Server - sql

I have a stored procedure as below. Now I want to modify it so that it returns the value as a comma separated list.
CREATE PROCEDURE [dbo].[CUST_Notification_GetCompaniesFromEmails]
#EmailValue AS NTEXT
AS
SELECT DISTINCT
ISNULL(CompanyAddressesContacts.FirstName, '') + ' ' +
ISNULL(CompanyAddressesContacts.LastName, '') AS ContactName
,ISNULL(dbo.Companies.CompanyName,'') AS CompanyName,
CASE
WHEN CompanyName = 'Meraas Development' THEN '1'
WHEN CompanyName = 'Samsung C&T' THEN '2'
ELSE '3'
END AS id
FROM
dbo.CompanyAddressesContacts
INNER JOIN
dbo.Companies ON dbo.CompanyAddressesContacts.CompanyId = Companies.Id
WHERE
dbo.CompanyAddressesContacts.Email IN
(SELECT [value]
FROM dbo.SplitWords(#EmailValue,';'))
The above code returns values as a normal select statement would do . Now I want a modification in the code so that it returns a value as comma-separated list.
Thanks!

Try like this
DECLARE #List VARCHAR(8000)
SELECT #List = COALESCE(#List + ',', '') + CAST(OfferID AS VARCHAR)
FROM Emp
WHERE EmpID = 23
SELECT #List

Try to use SplitWords method as
dbo.SplitWords(#EmailValue +',','')
Have a visit
http://msmvps.com/blogs/robfarley/archive/2007/04/08/coalesce-is-not-the-answer-to-string-concatentation-in-t-sql.aspx
http://oops-solution.blogspot.in/2011/11/sql-server-convert-table-column-data.html

Related

Mask values in a SQL Query string for SQL Server

I am a SQL Server DBA. I would like to write a procedure which I can provide to rest of my team where they can view the text for currently running queries on the server (Similar to how we view in sp_who2) but with all the values masked.
Examples:
Query text
Query text after Masking
Select * from sometable where rating = '4'
Select * from sometable where rating = '****'
Select name, id from sometable where id = '3233'
Select name, id from sometable where id = '****'
UPDATE Customers SET ContactName = 'Alfred Schmidt' WHERE CustomerID = 1;
UPDATE Customers SET ContactName = '****' WHERE CustomerID = ****;
INSERT INTO Customers (CustomerName, ContactName) VALUES ('Cardinal', 'Tom B. Erichsen');
INSERT INTO Customers (CustomerName, ContactName) VALUES ('*****', '****');
If I understand correctly your issue.
You can use this query:
select
r.session_id,
r.status,
r.command,
r.cpu_time,
r.total_elapsed_time,
t.text
from sys.dm_exec_requests as r
cross apply sys.dm_exec_sql_text(r.sql_handle) as t
e.g.
I run it on my SQL server right now:
(#P1 nvarchar(5),#P2 bigint,#P3 int,#P4 numeric(28, 12),#P5 nvarchar(5),#P6 datetime,#P7 datetime)
SELECT SUM(A.SETTLEAMOUNTCUR) FROM CUSTSETTLEMENT A,CUSTTRANS B WHERE ((A.DATAAREAID=#P1) AND (((A.TRANSRECID=#P2) AND (A.CANBEREVERSED=#P3)) AND (A.SETTLEAMOUNTCUR<>#P4))) AND ((B.DATAAREAID=#P5) AND (((B.RECID=A.OFFSETRECID) AND (B.TRANSDATE>=#P6)) AND (B.TRANSDATE<=#P7)))
All variables are hidden.
You could try some XML-trickery to handle the strings.
First replace all single quotes with an empty tag <X/> to get a XML that looks like this.
INSERT INTO Customers (CustomerName, ContactName)
VALUES (<X />Cardinal<X />, <X />Tom B. Erichsen<X />);
Then you shred the xml to get the text nodes and the node numbers where mod 2 is 0 is the ones you want to mask.
After that you can rebuild your query string using the mask values.
I have not found a way to deal with numbers other then removing all numbers from the query using Translate or nested replace and that will of course also remove numbers from table names and column names as well.
You could try something like this.
declare #S nvarchar(max);
declare #X xml;
set #S = N'UPDATE Customers SET ContactName = ''Alfred Schmidt'' WHERE CustomerID = 1;';
set #X = replace(#S, '''', '<X/>');
with C as
(
select T.X.value('.', 'nvarchar(max)') as V,
row_number() over(order by T.X) as RN
from #X.nodes('text()') as T(X)
)
select #S = (
select case when C.RN % 2 = 0 then '''*****''' else C.V end
from C
order by C.RN
for xml path(''), type
).value('text()[1]', 'nvarchar(max)');
set #S = translate(#S, '0123456789', '**********')
print #S;
Result:
UPDATE Customers SET ContactName = '*****' WHERE CustomerID = *;
Note: Just realized that this solution does not handle the cases where the string values contains single quotes but I think this is something that possibly can inspire more robust solution so I will leave it here.
sys.sp_get_query_template
fiddle
declare #t nvarchar(max), #p nvarchar(max);
declare #q nvarchar(max) = 'UPDATE Customers SET ContactName = N''Alfred Schmidt'' WHERE CustomerID = 1 AND Rate = 0.75 AND Rver = 0x0102 AND DateCreated = dateadd(day, -10, ''202z0818'')';
exec sys.sp_get_query_template #querytext = #q, #templatetext = #t OUTPUT, #parameters = #p OUTPUT;
select p,
case when tp like '%int' then cast('****' as nvarchar(40))
when tp like 'decimal(%' or tp like 'numeric(%' then '**.**'
when tp like '%binary(%' then '0x****'
when tp like 'n%char%' then 'N''****'''
else '''****'''
end as rv
into #t
from
(
select *, '#'+left(s.value, charindex(' ', s.value+' ')-1) as p, stuff(s.value, 1, charindex(' ', s.value), '') as tp
from string_split(replace(#p, ',#', '#'), '#') as s
where s.value <> ''
) as ss;
update #t
set #t = replace(#t, p, rv);
select #q union all select #t;

query not retrieving values returned from split string

I generate comma seperated string and add single quite to each numbers
Here is how i do it
DECLARE #IDs NVARCHAR(max)
SELECT #IDs = COALESCE(#IDs +',', '') + ''''
+ Cast([mynos] AS NVARCHAR(255)) + ''''
FROM mytable
WHERE id = 22
If i print variable #IDs then i get below output
'78888','3333','1222'
When i use same variable in this query then query doesnt return any value
SELECT *
FROM table1
WHERE ids IN ( #IDs )
How to fix this?
It doesn't work as your query is effectively doing this:
SELECT *
FROM TABLE
WHERE Ids IN ('''78888'',''3333',''1222''');
Which would also be equivalent to:
SELECT *
FROM TABLE
WHERE Ids = '''78888'',''3333',''1222''';
If you want to do the query as you have done, you'll need to split your delomited data out again. As you're using SQL Server 2012, you can't make use of STRING_SPLIT, so you'll need to a different one; such as Jeff Moden's DelimitedSplit8K. Then you can do:
SELECT *
FROM TABLE
WHERE IDs IN (SELECT items
FROM dbo.DelimitedSplit8K (#IDs,','));
However, why are you not simply doing...
SELECT *
FROM TABLE T
WHERE EXISTS (SELECT 1
FROM myTable mT
WHERE mT.Id = 22
AND mT.myNos = T.Ids);
You can use dynamic query #id is string variable not multi-value argument
DECLARE #IDs nVARCHAR(MAX)
SELECT #IDs = COALESCE(#IDs +',' ,'') + '''' + CAST([myNos] AS nVARCHAR(255)) + ''''
FROM myTable WHERE Id = 22
DECLARE #query nVARCHAR(MAX)
SET #query = "Select * from table1 where Ids in ("+#IDs+")"
EXECUTE sp_executesql #query
I tried below and it worked
select * from table1 where id in (select mynos from mytable where id = 22)
Thanks to #Larnu for giving me idea

SQL Server - COALESCE WHEN NOTHING RETURNS , GET DEFAULT VALUE

I'm trying to use Coalesce function in SQL Server to concatente multiple names. But when the conditon in the query returns no rows or nothing, I need to return a default value. I tried some condition using case statement but I can't figure it out what I missed.
declare #Names varchar(max) = '',
#Key varchar(max) = 'ABC'
select #Names = COALESCE(#Names, '') + isnull(T0.A, #Key) + ', '
from TData P
left join TNames T0 on T0.C + '\' + T0.D = P.#Key
where OBID=581464
and ID < 1432081
select #Names
You can do it with 2 minor changes to your current code, but I suspect this is an XYProblem, and you might benefit more from editing your question to include sample data and desired results (so perhaps we can suggest a better solution).
Anyway, what I had in mind is this:
declare #Names varchar(max), -- remove the = '', so that #Names starts as null
#Key varchar(max) = 'ABC'
select #Names = COALESCE(#Names, '') + isnull(T0.A, #Key) + ', '
from TData P
left join TNames T0 on T0.C + '\' + T0.D = P.#Key -- ' (This is just to fix the coding colors)
where OBID=581464
and ID < 1432081
select COALESCE(#Names, 'default value') -- since #Names started as null, and the query did not return any results, it's still null...

T-SQL - remove chars from string beginning from specific character

from table I retrieves values, for example,
7752652:1,7752653:2,7752654:3,7752655:4
or
7752941:1,7752942:2
i.e. string may contain any quantity of substrings.
What I need: remove all occurrences of characters from char ':' to a comma char.
For example,
7752652:1,7752653:2,7752654:3,7752655:4
should be
7752652,7752653,7752654,7752655
How do it?
Replace : with start tag <X>.
Replace , with end tag </X> and an extra comma.
Add an extra end tag to the end </X>.
That will give you a string that look like 7752941<X>1</X>,7752942<X>2</X>.
Cast to XML and use query(text()) to get the root text values.
Cast the result back to string.
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table T
(
C varchar(100)
)
insert into T values
('7752652:1,7752653:2,7752654:3,7752655:4'),
('7752941:1,7752942:2')
Query 1:
select cast(cast(replace(replace(T.C, ':', '<X>'), ',', '</X>,')+'</X>' as xml).query('text()') as varchar(100)) as C
from T
Results:
| C |
|---------------------------------|
| 7752652,7752653,7752654,7752655 |
| 7752941,7752942 |
declare #query varchar(8000)
select #query= 'select '+ replace (
replace('7752652:1,7752653:2,7752654:3,7752655:4',',',' t union all select ')
,':',' t1 , ')
exec(';with cte as ( '+#query+' ) select cast(t1 as varchar)+'','' from cte for xml path('''')')
Try this:
DECLARE #Data VARCHAR(100) = '7752652:1,7752653:2,7752654:3,7752655:4'
DECLARE #Output VARCHAR(100) = ''
WHILE CHARINDEX(':', #Data) > 0
BEGIN
IF LEN(#Output) > 0 SET #Output = #Output + ','
SET #Output = #Output + LEFT(#Data, CHARINDEX(':', #Data)-1)
SET #Data = STUFF(#Data,
1,
(CASE CHARINDEX(',', #Data)
WHEN 0 THEN LEN(#Data)
ELSE CHARINDEX(',', #Data)
END) - CHARINDEX(':', #Data),
'')
END
SELECT #Output AS Result -- 7752652,7752653,7752654,7752655
Hope this will help.
I borrowed the Splitter function from here. You could use any delimiter parser you may already be using.
Parse the string to table values
Used Substring function to remove values after ':'
Use For xml to re-generate CSV
Test Data:'
IF OBJECT_ID(N'tempdb..#temp')>0
DROP TABLE #temp
CREATE TABLE #temp (id int, StringCSV VARCHAR(500))
INSERT INTO #temp VALUES ('1','7752652:1,7752653:2,7752654:3,7752655:4')
INSERT INTO #temp VALUES ('2','7752656:1,7752657:3,7752658:4')
INSERT INTO #temp VALUES ('3','7752659:1,7752660:2')
SELECT * FROM #temp t
Main Query:
;WITH cte_Remove(ID, REMOVE) AS
(
SELECT y.id AS ID,
SUBSTRING(fn.string, 1, CHARINDEX(':', fn.string) -1) AS Removed
FROM #temp AS y
CROSS APPLY dbo.fnParseStringTSQL(y.StringCSV, ',') AS fn
)
SELECT DISTINCT ID,
STUFF(
(
SELECT ',' + REMOVE
FROM cte_Remove AS t2
WHERE t2.ID = t1.ID
FOR XML PATH('')
),1,1,'') AS col2
FROM cte_Remove AS t1
Cleanup Test Data:
IF OBJECT_ID(N'tempdb..#temp') > 0
DROP TABLE #temp
I solved this problem with CLR function. It is more quickly and function can be used in complex queries
public static SqlString fnRemoveSuffics(SqlString source)
{
string pattern = #":(\d+)";
string replacement = "";
string result = Regex.Replace(source.Value, pattern, replacement);
return new SqlString(result);
}

Convert SQL Server result set into string

I am getting the result in SQL Server as
SELECT StudentId FROM Student WHERE condition = xyz
I am getting the output like
StudentId
1236
7656
8990
........
The output parameter of the stored procedure is #studentId string and I want the return statement as
1236, 7656, 8990.
How can I convert the output in the single string?
I am returning single column [ie. StudentId]
Test this:
DECLARE #result NVARCHAR(MAX)
SELECT #result = STUFF(
( SELECT ',' + CONVERT(NVARCHAR(20), StudentId)
FROM Student
WHERE condition = abc
FOR xml path('')
)
, 1
, 1
, '')
DECLARE #result varchar(1000)
SELECT #result = ISNULL(#result, '') + StudentId + ',' FROM Student WHERE condition = xyz
select substring(#result, 0, len(#result) - 1) --trim extra "," at end
Use the COALESCE function:
DECLARE #StudentID VARCHAR(1000)
SELECT #StudentID = COALESCE(#StudentID + ',', '') + StudentID
FROM Student
WHERE StudentID IS NOT NULL and Condition='XYZ'
select #StudentID
Both answers are valid, but don't forget to initializate the value of the variable, by default is NULL and with T-SQL:
NULL + "Any text" => NULL
It's a very common mistake, don't forget it!
Also is good idea to use ISNULL function:
SELECT #result = #result + ISNULL(StudentId + ',', '') FROM Student
Use the CONCAT function to avoid conversion errors:
DECLARE #StudentID VARCHAR(1000)
SELECT #StudentID = CONCAT(COALESCE(#StudentID + ',', ''), StudentID)
FROM Student
WHERE StudentID IS NOT NULL and Condition='XYZ'
select #StudentID
The following is a solution for MySQL (not SQL Server), i couldn't easily find a solution to this on stackoverflow for mysql, so i figured maybe this could help someone...
ref: https://forums.mysql.com/read.php?10,285268,285286#msg-285286
original query...
SELECT StudentId FROM Student WHERE condition = xyz
original result set...
StudentId
1236
7656
8990
new query w/ concat...
SELECT group_concat(concat_ws(',', StudentId) separator '; ')
FROM Student
WHERE condition = xyz
concat string result set...
StudentId
1236; 7656; 8990
note: change the 'separator' to whatever you would like
GLHF!
This one works with NULL Values in Table and doesn't require substring operation at the end. COALESCE is not really well working with NULL values in table (if they will be there).
DECLARE #results VARCHAR(1000) = ''
SELECT #results = #results +
ISNULL(CASE WHEN LEN(#results) = 0 THEN '' ELSE ',' END + [StudentId], '')
FROM Student WHERE condition = xyz
select #results
The answer from brad.v is incorrect! It won't give you a concatenated string.
Here's the correct code, almost like brad.v's but with one important change:
DECLARE #results VarChar(1000)
SELECT #results = CASE
WHEN #results IS NULL THEN CONVERT( VarChar(20), [StudentId])
ELSE #results + ', ' + CONVERT( VarChar(20), [StudentId])
END
FROM Student WHERE condition = abc;
See the difference? :) brad.v please fix your answer, I can't do anything to correct it or comment on it 'cause my reputation here is zero. I guess I can remove mine after you fix yours. Thanks!
Use STRING_AGG:
SELECT STRING_AGG(sub.StudentId, ',') FROM
(select * from dbo.Students where Name = 'Test3') as sub
If you want to use e.g ORDER BY:
SELECT STRING_AGG(sub.StudentId, ',') WITHIN GROUP(Order by StudentId) FROM
(select * from dbo.Students where Name = 'Test3') as sub
or a single select statement...
DECLARE #results VarChar(1000)
SELECT #results = CASE
WHEN #results IS NULL THEN CONVERT( VarChar(20), [StudentId])
ELSE ', ' + CONVERT( VarChar(20), [StudentId])
END
FROM Student WHERE condition = abc;
Assign a value when declaring the variable.
DECLARE #result VARCHAR(1000) ='';
SELECT #result = CAST(StudentId AS VARCHAR) + ',' FROM Student WHERE condition = xyz