Convert ToUrlString Extension to T-SQL - c#

I need to create a report of all our items with a URL link to the webpage that is generated for each item. The website uses the .ToUrlString extension (http://www.extensionmethod.net/1935/csharp/string/tourlstring) to generate the link address. Is there a way to get the same result in SQL? The database is SqlServer 2008R2.
This is the code for the extension:
public static string ToUrlString(this string str) {
if (String.IsNullOrEmpty(str)) return "";
// Unicode Character Handling: http://blogs.msdn.com/b/michkap/archive/2007/05/14/2629747.aspx
string stFormD = str.Trim().ToLowerInvariant().Normalize(NormalizationForm.FormD);
var sb = new StringBuilder();
foreach (char t in
from t in stFormD
let uc = CharUnicodeInfo.GetUnicodeCategory(t)
where uc != UnicodeCategory.NonSpacingMark
select t) {
sb.Append(t);
}
return Regex.Replace(sb.ToString().Normalize(NormalizationForm.FormC), "[\\W\\s]{1,}", "-").Trim('-');
}
This function turns "Cups & Saucers" into "cups-saucers" and
"Spoon-Style Long Sticks-Set of 4" into "spoonstyle-long-sticksset-of-4"
EDIT:
Because it was 2am, I was tired, and was pretty sure I wasn't going to successfully teach my self anything new at that time, I created a long-azz REPLACE() statement to filter out what I needed. That's below in a separate answer for anyone that decides they want to do it the hard way...
The correct way is to create a CLR function, as suggested by #DaleBurrell in the comments. This article explains it, step by step: SQL CLR Functions:

UPDATED ON 20190424 BASED ON OP'S COMMENTS BELOW
This is easy as pie using T-SQL. Using PatReplace8K and a function I just wrote to remove repeated instances of a specific character (RemoveDupes8K) you could do this:
DECLARE #string VARCHAR(1000) = 'Spoon-Style Long Sticks-Set of 4'
SELECT newstring = LOWER(r.NewString)
FROM dbo.patreplace8k(REPLACE(#string,'-',''),'[^a-zA-Z0-9]','-') AS f
CROSS APPLY dbo.RemoveDupes8K(f.NewString,'-') AS r;
Returns: spoonstyle-long-sticksset-of-4
Here's what's going on. PatReplace8k is taking the input string (#string) and replacing all non-alphanumeric characters with a hyphen. RemoveDupes8K takes that new string and replaces duplicate hyphens with one.
Here's the functions:
CREATE FUNCTION dbo.RemoveDupes8K
(
#string VARCHAR(8000),
#char CHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT NewString =
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(#string)),
REPLICATE(#char,33),#char),
REPLICATE(#char,17),#char),
REPLICATE(#char,9), #char),
REPLICATE(#char,5), #char),
REPLICATE(#char,3), #char),
REPLICATE(#char,2), #char),
REPLICATE(#char,2), #char);
GO
CREATE FUNCTION dbo.PatReplace8K
(
#string VARCHAR(8000),
#pattern VARCHAR(50),
#replace VARCHAR(1)
)
/*****************************************************************************************
Purpose:
Given a string (#String), a pattern (#Pattern), and a replacement character (#Replace)
PatReplace8K will replace any character in #String that matches the #Pattern parameter
with the character, #Replace.
Usage:
--===== Basic Syntax Example
SELECT pr.NewString
FROM dbo.PatReplace8K(#String,#Pattern,#Replace);
--===== Replace numeric characters with a "*"
SELECT pr.NewString
FROM dbo.PatReplace8K('My phone number is 555-2211','[0-9]','*') pr;
--==== Using againsts a table
DECLARE #table TABLE(OldString varchar(40));
INSERT #table VALUES
('Call me at 555-222-6666'),
('phone number: (312)555-2323'),
('He can be reached at 444.665.4466');
SELECT t.OldString, pr.NewString
FROM #table t
CROSS APPLY dbo.PatReplace8K(t.oldstring,'[0-9]','*') pr;
Programmer Notes:
1. Required SQL Server 2008+
2. #Pattern IS case sensitive but can be easily modified to make it case insensitive
3. There is no need to include the "%" before and/or after your pattern since since we
are evaluating each character individually
4. Certain special characters, such as "$" and "%" need to be escaped with a "/"
like so: [/$/%]
Revision History:
Rev 00 - 10/27/2014 Initial Development - Alan Burstein
Rev 01 - 10/29/2014 Mar 2007 - Alan Burstein
- Redesigned based on the dbo.STRIP_NUM_EE by Eirikur Eiriksson
(see: http://www.sqlservercentral.com/Forums/Topic1585850-391-2.aspx)
- change how the cte tally table is created
- put the include/exclude logic in a CASE statement instead of a WHERE clause
- Added Latin1_General_BIN Colation
- Add code to use the pattern as a parameter.
Rev 02 - 20141106
- Added final performane enhancement (more cudo's to Eirikur Eiriksson)
- Put 0 = PATINDEX filter logic into the WHERE clause
Rev 03 - 20150516
- Updated to deal with special XML characters
Rev 04 - 20170320
- changed #replace from char(1) to varchar(1) to address how spaces are handled
*****************************************************************************************/
RETURNS TABLE WITH SCHEMABINDING
AS
RETURN
WITH
E1(N) AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS E1(N)),
iTally(N) AS
(
SELECT TOP (LEN(#String)) CHECKSUM(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))
FROM E1 a,E1 b,E1 c,E1 d
)
SELECT NewString =
((
SELECT
CASE
WHEN PATINDEX(#Pattern,SUBSTRING(#String COLLATE Latin1_General_BIN,N,1)) = 0
THEN SUBSTRING(#String,N,1)
ELSE #replace
END
FROM iTally
FOR XML PATH(''), TYPE
).value('.[1]','varchar(8000)'));
GO

As promised, for anyone that wanted to see the REPLACE() method...
SELECT 'Product' Source
,prd.[id]
,prd.[name] as Title
,CAST(prd.[description] as varchar(max)) as [Description]
,'/' + lower(replace(replace(RTrim(par.name), ' &', ''), ' ' ,'-') + '/' + REPLACE(replace(replace(RTrim(child.name),'\',''), ' &', ''), ' ', '-') + '/' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(LTRIM(RTrim(prd.name)),'.',''),'$',''),'/',''),')',''),'(',''),'-',''),',',''), '''',''), '"', ''), ' &', ''),' ',' '), ' ' , '-') ) as Link
,prd.[upc] as GTIN
,prd.[active]
FROM [dbo].[Products] prd
INNER join [dbo].[CategoryProducts] catprd ON catprd.productId = prd.id
INNER JOIN [dbo].[Categories] child on child.Id = catprd.categoryId
INNER join [dbo].[Categories] par on par.id = child.parentCategoryId

Related

Multiple word search in a single table column

I am create a simple textbox that allow to single word compare into the table.
if i am give multiple keyword into the textbox only one word working on multiple text its not fetch record.
my database table is-
id text keyword
1 Somting with keyword1 & Keyword3 keyword1, keyword3
2 Somting with keyword2 & Keyword3 Keyword2, Keyword3
Search string is like-
What is Keyword3 with keyword1
Desired result is
Somting with keyword1 & Keyword3 show in record
using c# sql
I am trying
Create PROCEDURE [dbo].[spGetChatAutofill]
(
#term VARCHAR(255)
)
AS
begin
select id , text, keyword
from tbl
where keyword in (select keyword from fn_stringSplit(#term) ss where keyword like '%'+SplitValue+'%')
end
and for function-
ALTER function [dbo].[fn_stringSplit](#StringSplit varchar(max))
returns #table table(SplitValue varchar(10) not null)
as
begin
Declare #StartVal int
Declare #endVal int
set #StringSplit = #StringSplit + ' '
set #StartVal = 1
set #endVal = 1
while #endVal >= 0
begin
set #endVal = charindex(' ',#StringSplit,#StartVal);
insert into #table select SUBSTRING(#StringSplit,#StartVal,#endVal-1)
set #StringSplit= SUBSTRING(#StringSplit,#endVal+1, LEN(#StringSplit))
if #StringSplit = '' set #endVal= -1
end
return
end
First , Sql Server 2017 has STRING_SPLIT.
Search rows with all requested keywords present procedure.
Create PROCEDURE [dbo].[spGetChatAutofill]
(
#term VARCHAR(255)
)
AS
begin
select id, text, keyword
from tbl
where not exists(
select 1
from STRING_SPLIT(#term, ' ') terms
where tbl.keyword not like '%'+terms.value+'%')
end
Note this may match partial keywords. For example
a call with an argument "key word" will successfully find "keyword1, keyword2" in the table.

What is a fastest way to get words from T-SQL datatable?

I have a SQL Server 2008 R2 datatable dbo.Forum_Posts with columns Subject (nvarchar(255)) andBody (nvarchar(max)).
I would like to get all words with length >= 3 from columns Subject and Body and insert them into datatable dbo.Search_Word (column Word, nvarchar(100)) and datatable dbo.SearchItem (column Title (nvarchar(200)).
I also want to get new generated SearchWordsID (primary key, autoincrement, int) from dbo.Search_Word, and SearchItemID (primary key, autoincrement,int) from dbo.SearchItem, and insert them into datatable dbo.SearchItemWord (columns SearchWordsID (foreign key,int, not null) and SearchItemID (foreign key,int,not null).
What is a fastest way to do this in T-SQL? Or I have to use C#? Thank you in advance for any help.
As requested, this will keep the ID's. So you will get a DISTINCT list of works BY id.
Slightly different approach than the first answer, but easily achieved via the Outer Apply
**
You must edit the initial query Select KeyID=[YourKeyID],Words=[YourField1]+' '+[YourField2] from [YourTable]
**
Declare #String varchar(max) = ''
Declare #Delimeter varchar(25) = ' '
-- Generate and Strip special characters
Declare #StripChar table (Chr varchar(10));Insert Into #StripChar values ('.'),(','),('/'),('('),(')'),(':') -- Add/Remove as needed
-- Generate Base Data and Expand via Outer Apply
Declare #XML xml
Set #XML = (
Select A.KeyID
,B.Word
From ( Select KeyID=[YourKeyID],Words=[YourField1]+' '+[YourField2] from [YourTable]) A
Outer Apply (
Select Word=split.a.value('.', 'varchar(150)')
From (Select Cast ('<x>' + Replace(A.Words, #Delimeter, '</x><x>')+ '</x>' AS XML) AS Data) AS A
Cross Apply data.nodes ('/x') AS Split(a)
) B
For XML RAW)
-- Convert XML to varchar(max) for Global Search & Replace (could be promoted to Outer Appy)
Select #String = Replace(Replace(cast(#XML as varchar(max)),Chr,' '),' ',' ') From #StripChar
Select #XML = cast(#String as XML)
Select Distinct
KeyID = t.col.value('#KeyID', 'int')
,Word = t.col.value('#Word', 'varchar(150)')
From #XML.nodes('/row') AS t (col)
Where Len(t.col.value('#Word', 'varchar(150)'))>3
Order By 1
Returns
KetID Word
0 UNDEF
0 Undefined
1 HIER
1 System
2 Control
2 UNDEF
3 JOBCONTROL
3 Market
3 Performance
...
87 Analyitics
87 Market
87 UNDEF
88 Branches
88 FDIC
88 UNDEF
...
You're going to need T-SQL to do the inserting into your tables. Your biggest challenge is going to be splitting the posts into words.
My suggestion would be to read the posts into C#, split each post into words (you can use the Split method to split on spaces or punctuation), filter the collection of words, and then execute your inserts from C#.
You can avoid using T-SQL directly if you use Entity Framework or a similar ORM.
Don't try to use T-SQL to split your posts into words unless you really want a totally SQL solution and are willing to take time to perfect it. And, yes, it will be slow: T-SQL is not fast at string operations.
You can also investigate full text indexing, which I believe has support for search keywords.
Perhaps this will help
Declare #String varchar(max) = ''
Declare #Delimeter varchar(25) = ' '
Select #String = #String + ' '+Words
From (
Select Words=[YourField1]+' '+[YourField2] from [YourTable]
) A
-- Generate and Strip special characters
Declare #StripChar table (Chr varchar(10));Insert Into #StripChar values ('.'),(','),('/'),('('),(')'),(':') -- Add/Remove as needed
Select #String = Replace(Replace(#String,Chr,' '),' ',' ') From #StripChar
-- Convert String into XML and Split Delimited String
Declare #Table Table (RowNr int Identity(1,1), String varchar(100))
Declare #XML xml = Cast('<x>' + Replace(#String,#Delimeter,'</x><x>')+'</x>' as XML)
Insert Into #Table Select String.value('.', 'varchar(max)') From #XML.nodes('x') as T(String)
-- Generate Final Resuls
Select Distinct String
From #Table
Where Len(String)>3
Order By 1
Returns (sample)
String
------------------
Access
Active
Adminstrators
Alternate
Analyitics
Applications
Branches
Cappelletti
City
Class
Code
Comments
Contact
Control
Daily
Data
Date
Definition
Deleted
Down
Email
FDIC
Variables
Weekly

Unpivot on a column that has dash on it

This is my query in SQL Server 2008 R2:
INSERT VMP_Staging_Products_Temporary
(FRM_ID, ITEM_ID, ItemDesc, NUMBER_ITEM)
SELECT upvt.FARM_ID,
p.REC_ITEM_CODE,
p.REC_ITEM_NAME_A,
upvt.NUMBER_ITEM
FROM VMP_Staging AS f
UNPIVOT
(
NUMBER_ITEM
FOR ItemDesc IN ([KHALAS], [FARDH], [OTHER_LULU], [KHENAIZI], [BOUMAAN], [BARHI], [JESH_KHARMA], [REZIZ]
, [JABRI], [ANBARET_AL_MADINA], [SHISHI], [DABBAS], [NABTET_SAIF], [KHEDRAWI], [HILALI], [MAKTOUMY]
, [NAMISHI], [SULTANAH], [BAQLAT_AL_TAWAA], [BAQLAT_AL_DAHLA], [BAQLAT_AL_RARENJA], [SUKARY], [SAQEI], [ABU_ZEBED]
, [MAJDOUL], [SHABIBI], [YOUWANI], [YARDI], [KHADI], [HATIMI], [NEGHAL], [OTHER_SAYER])
) AS upvt
INNER JOIN REC_ITEM AS P
on p.REC_ITEM_NAME_A = upvt.ItemDesc
As you see, I am doing a join between REC_ITEM table and the result of the unpivot.
The problem happens in the column names that has _ like ABU_ZEBED where the value in the REC_ITEM is abu zebed
How can I solve it please?
Update 1
If there is a way to do that using c# code, it is good too. because this is a stored procedure called from c# so if there is solution to deal with this in c#, I would like that too.
Depends on whether it's a regular pattern or not, but at least:
INNER JOIN REC_ITEM AS P
on replace(p.REC_ITEM_NAME_A, ' ', '_') = upvt.ItemDesc
Performance will be terrible, though.
You can try joining in multiple ways:
on p.REC_ITEM_NAME_A = upvt.ItemDesc or
p.REC_ITEM_NAME_A = replace(upvt.ItemDesc, '_', ' ')
Alternatively, remove both spaces and underscores from both sides:
on replace(replace(p.REC_ITEM_NAME_A, '_', ''), ' ', '') =
replace(replace(upvt.ItemDesc, '_', ''), ' ', '')

What is the best way to convert this to SQL

I know for the Database Guru's here this should be a doddle. I have a Field in my database in the format of ' A/B/C/D/E/F '
The format is irrelevant I generally need the last two parts so for the above it would be
'EF'
But if I had another string
AB/CD/EF/GH == EFGH
And I am looking to getting the last two parts to return like this 'EFGH'
Does anyone know an SQL Function I can do that will split this
I am using Microsoft SQL Server 2012 - I Hope this helps,
Here is C# Code.
var myText = "A/B/C/D/E/F";
var identificationArray = myText.Split('/');
if(identificationArray.Length >= 2)
{
var friendlyId = identificationArray[identificationArray.Length - 2] + identificationArray[identificationArray.Length - 1];
return friendlyId;
}
return "";
Here is one answer that searches a string in reverse order for the second forward slash and returns that substring with forward slashes removed:
declare #s varchar(20)
set #s = 'A/B/C/D/E/F'
-- result: 'EF'
select reverse(replace(left(reverse(#s), charindex('/', reverse(#s), charindex('/', reverse(#s)) + 1)), '/', ''))
set #s = 'AB/CD/EF/GH'
-- result: 'EFGH'
select reverse(replace(left(reverse(#s), charindex('/', reverse(#s), charindex('/', reverse(#s)) + 1)), '/', ''))
Testing this with a couple of other inputs:
set #s = '/AB/CD' -- result: 'ABCD'
set #s = 'AB/CD' -- result: an empty string '' -- you may not want this result
set #s = 'AB' -- result: an empty string ''
Here is a ridiculously complicated way to do the same thing with a series of common table expressions (CTEs). Credit goes to Itzik Ben-Gan for the CTE technique to generate a tally table using cross-joins:
declare #s varchar(50)
set #s = 'A/B/C/D/E/F/G'
--set #s = 'AB/CD/EF/GH'
--set #s = 'AB/CD'
--set #s = 'ABCD/EFGH/IJKL'
--set #s = 'A/B'
-- set #s = 'A'
declare #result varchar(50)
set #result = ''
;with
-- cross-join a meaningless set of data together to create a lot of rows
Nbrs_2 (n) AS (SELECT 1 UNION SELECT 0 ),
Nbrs_4 (n) AS (SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2),
Nbrs_16 (n) AS (SELECT 1 FROM Nbrs_4 n1 CROSS JOIN Nbrs_4 n2),
Nbrs_256 (n) AS (SELECT 1 FROM Nbrs_16 n1 CROSS JOIN Nbrs_16 n2),
Nbrs_65536(n) AS (SELECT 1 FROM Nbrs_256 n1 CROSS JOIN Nbrs_256 n2),
Nbrs (n) AS (SELECT 1 FROM Nbrs_65536 n1 CROSS JOIN Nbrs_65536 n2),
-- build a table of numbers from the data above; this is insanely fast
nums(n) as
(
select row_number() over(order by n) from Nbrs
),
-- split the string into separate rows per letter
letters(n, c) as
(
select n, substring(#s, n, 1)
from nums
where n < len(#s) + 1
),
-- count the slashes from the rows in descending order
-- the important slash is the second one from the end
slashes(n, num) as
(
select n, ROW_NUMBER() over (order by n desc)
from letters
where c = '/'
)
select #result = #result + c
from letters
where n > (select n from slashes where num = 2) -- get everything after the second slash
and c <> '/' -- and drop out the other slash
select #result
You need to reverse the string and find the 2nd occurrence of the / character. Once you have that it is pretty straight forward, just a lot of function calls to get the desired format
declare #test varchar(max);
set #test = 'b/b/a/v/d';
select
case
when charindex('/', reverse(#test), charindex('/', reverse(#test))+1) = 0 then ''
else replace(reverse(substring(reverse(#test), 0, charindex('/', reverse(#test), charindex('/', reverse(#test))+1))), '/', '')
end
I understand that you want to do this in SQL. But did you think about using SQL CLR User Defined Functions? It will execute faster than SQL. you anyways have the logic implemented in C# which definitely simpler than the logic in SQL.
Late to the party, but here is my attempt:
declare #text varchar(max), #reversedtext varchar(max)
select #text = 'AB/CD/EF/GH'
select #reversedtext = reverse(#text)
declare #pos1 int
declare #pos2 int
declare #pos3 int
select #pos1 = charindex('/', #reversedtext)
select #pos2 = charindex('/', replace(#reversedtext, left(#reversedtext, #pos1), ''))
select #pos3 = #pos1 + #pos2
select REPLACE(RIGHT(#text, #pos3), '/', '')

'group_concat' is not a recognized built-in function name [duplicate]

I'm trying to migrate a MySQL-based app over to Microsoft SQL Server 2005 (not by choice, but that's life).
In the original app, we used almost entirely ANSI-SQL compliant statements, with one significant exception -- we used MySQL's group_concat function fairly frequently.
group_concat, by the way, does this: given a table of, say, employee names and projects...
SELECT empName, projID FROM project_members;
returns:
ANDY | A100
ANDY | B391
ANDY | X010
TOM | A100
TOM | A510
... and here's what you get with group_concat:
SELECT
empName, group_concat(projID SEPARATOR ' / ')
FROM
project_members
GROUP BY
empName;
returns:
ANDY | A100 / B391 / X010
TOM | A100 / A510
So what I'd like to know is: Is it possible to write, say, a user-defined function in SQL Server which emulates the functionality of group_concat?
I have almost no experience using UDFs, stored procedures, or anything like that, just straight-up SQL, so please err on the side of too much explanation :)
No REAL easy way to do this. Lots of ideas out there, though.
Best one I've found:
SELECT table_name, LEFT(column_names , LEN(column_names )-1) AS column_names
FROM information_schema.columns AS extern
CROSS APPLY
(
SELECT column_name + ','
FROM information_schema.columns AS intern
WHERE extern.table_name = intern.table_name
FOR XML PATH('')
) pre_trimmed (column_names)
GROUP BY table_name, column_names;
Or a version that works correctly if the data might contain characters such as <
WITH extern
AS (SELECT DISTINCT table_name
FROM INFORMATION_SCHEMA.COLUMNS)
SELECT table_name,
LEFT(y.column_names, LEN(y.column_names) - 1) AS column_names
FROM extern
CROSS APPLY (SELECT column_name + ','
FROM INFORMATION_SCHEMA.COLUMNS AS intern
WHERE extern.table_name = intern.table_name
FOR XML PATH(''), TYPE) x (column_names)
CROSS APPLY (SELECT x.column_names.value('.', 'NVARCHAR(MAX)')) y(column_names)
I may be a bit late to the party but this method works for me and is easier than the COALESCE method.
SELECT STUFF(
(SELECT ',' + Column_Name
FROM Table_Name
FOR XML PATH (''))
, 1, 1, '')
SQL Server 2017 does introduce a new aggregate function
STRING_AGG ( expression, separator).
Concatenates the values of string expressions and places separator
values between them. The separator is not added at the end of string.
The concatenated elements can be ordered by appending WITHIN GROUP (ORDER BY some_expression)
For versions 2005-2016 I typically use the XML method in the accepted answer.
This can fail in some circumstances however. e.g. if the data to be concatenated contains CHAR(29) you see
FOR XML could not serialize the data ... because it
contains a character (0x001D) which is not allowed in XML.
A more robust method that can deal with all characters would be to use a CLR aggregate. However applying an ordering to the concatenated elements is more difficult with this approach.
The method of assigning to a variable is not guaranteed and should be avoided in production code.
Possibly too late to be of benefit now, but is this not the easiest way to do things?
SELECT empName, projIDs = replace
((SELECT Surname AS [data()]
FROM project_members
WHERE empName = a.empName
ORDER BY empName FOR xml path('')), ' ', REQUIRED SEPERATOR)
FROM project_members a
WHERE empName IS NOT NULL
GROUP BY empName
Have a look at the GROUP_CONCAT project on Github, I think I does exactly what you are searching for:
This project contains a set of SQLCLR User-defined Aggregate functions (SQLCLR UDAs) that collectively offer similar functionality to the MySQL GROUP_CONCAT function. There are multiple functions to ensure the best performance based on the functionality required...
To concatenate all the project manager names from projects that have multiple project managers write:
SELECT a.project_id,a.project_name,Stuff((SELECT N'/ ' + first_name + ', '+last_name FROM projects_v
where a.project_id=project_id
FOR
XML PATH(''),TYPE).value('text()[1]','nvarchar(max)'),1,2,N''
) mgr_names
from projects_v a
group by a.project_id,a.project_name
With the below code you have to set PermissionLevel=External on your project properties before you deploy, and change the database to trust external code (be sure to read elsewhere about security risks and alternatives [like certificates]) by running ALTER DATABASE database_name SET TRUSTWORTHY ON.
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.SqlServer.Server;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,
MaxByteSize=8000,
IsInvariantToDuplicates=true,
IsInvariantToNulls=true,
IsInvariantToOrder=true,
IsNullIfEmpty=true)]
public struct CommaDelimit : IBinarySerialize
{
[Serializable]
private class StringList : List<string>
{ }
private StringList List;
public void Init()
{
this.List = new StringList();
}
public void Accumulate(SqlString value)
{
if (!value.IsNull)
this.Add(value.Value);
}
private void Add(string value)
{
if (!this.List.Contains(value))
this.List.Add(value);
}
public void Merge(CommaDelimit group)
{
foreach (string s in group.List)
{
this.Add(s);
}
}
void IBinarySerialize.Read(BinaryReader reader)
{
IFormatter formatter = new BinaryFormatter();
this.List = (StringList)formatter.Deserialize(reader.BaseStream);
}
public SqlString Terminate()
{
if (this.List.Count == 0)
return SqlString.Null;
const string Separator = ", ";
this.List.Sort();
return new SqlString(String.Join(Separator, this.List.ToArray()));
}
void IBinarySerialize.Write(BinaryWriter writer)
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(writer.BaseStream, this.List);
}
}
I've tested this using a query that looks like:
SELECT
dbo.CommaDelimit(X.value) [delimited]
FROM
(
SELECT 'D' [value]
UNION ALL SELECT 'B' [value]
UNION ALL SELECT 'B' [value] -- intentional duplicate
UNION ALL SELECT 'A' [value]
UNION ALL SELECT 'C' [value]
) X
And yields: A, B, C, D
Tried these but for my purposes in MS SQL Server 2005 the following was most useful, which I found at xaprb
declare #result varchar(8000);
set #result = '';
select #result = #result + name + ' '
from master.dbo.systypes;
select rtrim(#result);
#Mark as you mentioned it was the space character that caused issues for me.
About J Hardiman's answer, how about:
SELECT empName, projIDs=
REPLACE(
REPLACE(
(SELECT REPLACE(projID, ' ', '-somebody-puts-microsoft-out-of-his-misery-please-') AS [data()] FROM project_members WHERE empName=a.empName FOR XML PATH('')),
' ',
' / '),
'-somebody-puts-microsoft-out-of-his-misery-please-',
' ')
FROM project_members a WHERE empName IS NOT NULL GROUP BY empName
By the way, is the use of "Surname" a typo or am i not understanding a concept here?
Anyway, thanks a lot guys cuz it saved me quite some time :)
2021
#AbdusSalamAzad's answer is the correct one.
SELECT STRING_AGG(my_col, ',') AS my_result FROM my_tbl;
If the result is too big, you may get error "STRING_AGG aggregation result exceeded the limit of 8000 bytes. Use LOB types to avoid result truncation." , which can be fixed by changing the query to this:
SELECT STRING_AGG(convert(varchar(max), my_col), ',') AS my_result FROM my_tbl;
For my fellow Googlers out there, here's a very simple plug-and-play solution that worked for me after struggling with the more complex solutions for a while:
SELECT
distinct empName,
NewColumnName=STUFF((SELECT ','+ CONVERT(VARCHAR(10), projID )
FROM returns
WHERE empName=t.empName FOR XML PATH('')) , 1 , 1 , '' )
FROM
returns t
Notice that I had to convert the ID into a VARCHAR in order to concatenate it as a string. If you don't have to do that, here's an even simpler version:
SELECT
distinct empName,
NewColumnName=STUFF((SELECT ','+ projID
FROM returns
WHERE empName=t.empName FOR XML PATH('')) , 1 , 1 , '' )
FROM
returns t
All credit for this goes to here:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/9508abc2-46e7-4186-b57f-7f368374e084/replicating-groupconcat-function-of-mysql-in-sql-server?forum=transactsql
For SQL Server 2017+, use STRING_AGG() function
SELECT STRING_AGG(Genre, ',') AS Result
FROM Genres;
Sample result:
Result
Rock,Jazz,Country,Pop,Blues,Hip Hop,Rap,Punk

Categories

Resources