There are some common tasks in SQL Server where past solutions have been a bit long and complicated code. As there are no special functions for these purposes in older versions of SQL Server, but in SQL Server 2016 and 2017 several new functions and features allow Developers to rewrite code in a much more compact way.
SolutionIn this tip, we are going to demonstrate some common problems requiring longer, complicated solutions in older versions of SQL Server. Then we will introduce a shorter solution for each task using new features that were released in either SQL Server 2016 and 2017 and will be noted in each solution below.
Create SQL Server Test EnvironmentLets create a test environment. This script creates a TestDB database with three tables with data: Student, Course and the StudentCourse table to map their relationships.
USE masterGO
--Database
CREATE DATABASE TestDB
GO
USE TestDB
GO
--Tables
CREATE TABLE Student
(
StudentID INT NOT NULL,
LoginName NVARCHAR(50),
PRIMARY KEY (StudentID)
)
GO
CREATE TABLE Course
(
CourseID INT NOT NULL,
CourseName NVARCHAR(50) UNIQUE,
PRIMARY KEY (CourseID)
)
GO
CREATE TABLE StudentCourse
(
StudentCourseID INT NOT NULL IDENTITY(1,1),
StudentID INT,
CourseID INT,
PRIMARY KEY (StudentCourseID),
CONSTRAINT UC_StudentID_CourseID UNIQUE(StudentID,CourseID),
)
GO
--Filling data
INSERT INTO Student(StudentID, LoginName)
VALUES(1, ' <a href="/cdn-cgi/l/email-protection" data-cfemail="9aeef5f7daeeffe9eeeff4f3ecffe8e9f3eee3b4f9f5f7">[email protected]</a> '),
(2, '<a href="/cdn-cgi/l/email-protection" data-cfemail="c6b5a7ab86b2a3b5b2b3a8afb0a3b4b5afb2bfe8a5a9ab">[email protected]</a> '),
(3, ' <a href="/cdn-cgi/l/email-protection" data-cfemail="583239363d182c3d2b2c2d36312e3d2a2b312c21763b3735">[email protected]</a>'),
(4, '<a href="/cdn-cgi/l/email-protection" data-cfemail="bcddd2d2fcc8d9cfc8c9d2d5cad9cecfd5c8c592dfd3d1">[email protected]</a>')
INSERT INTO Course(CourseID, CourseName)
VALUES(1, 'psychology'),
(2, 'physics'),
(3, 'chemistry'),
(4, 'astrology'),
(5, 'mathematics'),
(6, 'biology'),
(7, 'geography')
INSERT INTO StudentCourse(StudentID, CourseID)
VALUES(1, 1),
(1, 3),
(1, 5),
(2, 5),
(3, 1),
(3, 2),
(3, 3),
(3, 4),
(3, 5),
(3, 6),
(3, 7),
(4, 2),
(4, 4),
(4, 5) Rollup Rows and Create a Comma Separated List
Sometimes we need to represent values from several rows into one column by a using a comma-separated list. So, our first task will be for each student to get his/her login name and enrolled subjects in one row separated by a comma.
To get a list of students and the enrolled courses, we can simply use this code:
USE TestDBGO
--Getting Students' enrolled courses
SELECT s.LoginName, c.CourseName
FROM StudentCourse sc
INNER JOIN Student s ON sc.StudentID=s.StudentID
INNER JOIN Course c ON c.CourseID=sc.CourseID
This returns a table with two columns showing the mapping between student and course:

Old Way - Rollup rows using STUFF
Our task is to have a table with one row for each student with his/her login and all of his/her courses separated by a comma. In older versions of SQL Server, we can use STUFF with FOR XML PATH to solve the problem as follows:
USE TestDBGO
--USING STUFF and FOR XML PATH in older versions of SQL Server
SELECT
LoginName,
STUFF(( SELECT ',' + c.CourseName AS [text()]
FROM StudentCourse sc
INNER JOIN Course c ON c.CourseID=sc.CourseID
WHERE sc.StudentID = st.StudentID
FOR XML PATH('')
), 1, 1, '' ) AS StudentCourses
FROM Student st
We will receive the desired result:

New Way - Rollup rows using STRING_AGG
SQL Server 2017 introduces a new string function STRING_AGG. It is an aggregate function that concatenates the values of rows (expression) into a single string separated by the given separator. As you can guess, it is an ideal function for solving our task. We can rewrite the code in SQL Server 2017 in the following way:
USE TestDBGO
--SQL Server 2017 and later
SELECT s.LoginName, STRING_AGG(c.CourseName, ',') AS StudentCourses
FROM StudentCourse sc
INNER JOIN Student s ON sc.StudentID=s.StudentID
INNER JOIN Course c ON c.CourseID=sc.CourseID
GROUP BY s.LoginName
So, we have a simpler and more compact code to get the same result:

Replacing Leading and Trailing Spaces in a String
In our above example, you may have noticed that some of login names contain spaces. So our second task is to get the same result as in the previous task, but without spaces in the LoginName.
Old Way - Use LTRIM and RTRIM to remove unwanted leading and trailing spacesIn older versions of SQL Server, we use LTRIM and RTRIM functions to replace all spaces in a string:
USE TestDBGO
-- Using LTRIM with RTRIM in older versions of SQL Server (note this code will not work in older versions because STRING_AGG is used)
SELECT RTRIM(LTRIM(s.LoginName)) AS LoginName, STRING_AGG(c.CourseName, ',') AS StudentCourses
FROM StudentCourse sc
INNER JOIN Student s ON sc.StudentID=s.StudentID
INNER JOIN Course c ON c.CourseID=sc.CourseID
GROUP BY s.LoginName

New Way - Use TRIM to remove unwanted leading and trailing spaces
SSQL Server 2017 introduced the TRIM function, which removes blanks (or other specified characters) from both the beginning and end of the given string. So, in SQL Server 2017 the code above can be rewritten by using just the TRIM, instead of two separate functions:
USE TestDBGO
--SQL Server 2017 and later
SELECT TRIM(s.LoginName) AS LoginName, STRING_AGG(c.CourseName, ',') AS StudentCourses
FROM StudentCourse sc
INNER JOIN Student s ON sc.StudentID=s.StudentID
INNER JOIN Course c ON c.CourseID=sc.CourseID
GROUP BY s.LoginName Replacing Values in a String
Now, let’s assume that special characters in a string should be replaced by others. Let's say we have a list of subjects separated by the following symbols: , / - _ | . An example string is: 'psychology/physics,chemistry-astrology_mathematics|biology,geography' . It is required to use a comma as the only separator. In other words, any of these other symbols should be replaced with a comma.
Old Way - Replace values in a string using the REPLACE functionBefore SQL Server 2017 we can do this by nesting REPLACE functions several times for each of the symbols. Since we are replacing four different values, we need to use the REPLACE function four times as follows:
USE TestDBGO
--In older versions of SQL Server
DECLARE @str NVARCHAR(MAX)='psychology/physics,chemistry-astrology_mathematics|biology,geography'
SET @str=REPLACE(REPLACE(REPLACE(REPLACE(@str,'|',','),'_',','),'-',','),'/',',')
SELECT @str AS 'Subjects'
We will have the list of these subjects separated only by comma: