How to Use STRING_AGG() for Concatenating Rows into a Single Column in SQL Server

When working with SQL Server, you may encounter scenarios where you need to combine multiple row values into a single column. Prior to SQL Server 2017, this required using STUFF() with FOR XML PATH(), but now, the STRING_AGG() function provides a simpler approach.

What is STRING_AGG()?

The STRING_AGG() function concatenates values from multiple rows into a single string with a specified separator.

Basic Syntax:

SELECT STRING_AGG(column_name, ', ') AS concatenated_values
FROM table_name;
  • column_name: The column whose values you want to concatenate.
  • ', ': The separator used between values.

Example Usage

Consider a Customers table:

id name
1 Alice
2 Bob
3 Charlie

Using STRING_AGG(), we can concatenate the names:

SELECT STRING_AGG(name, ', ') AS customer_names
FROM Customers;

Result:

Alice, Bob, Charlie

Using STRING_AGG() with GROUP BY

You can also use STRING_AGG() within GROUP BY to aggregate data by a specific column. Consider an Orders table:

customer_id product
1 Laptop
1 Mouse
2 Keyboard
2 Monitor

To get a list of products purchased by each customer:

SELECT customer_id, STRING_AGG(product, ', ') AS purchased_products
FROM Orders
GROUP BY customer_id;

Result:

customer_id | purchased_products
------------|-------------------
1           | Laptop, Mouse
2           | Keyboard, Monitor

Sorting Values in STRING_AGG()

By default, STRING_AGG() does not guarantee an order. To enforce ordering, use WITHIN GROUP (ORDER BY column_name). Example:

SELECT STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY name) AS sorted_names
FROM Customers;

Key Benefits of STRING_AGG():

  • Eliminates complex workarounds like STUFF() with FOR XML PATH().
  • More readable and concise syntax.
  • Works efficiently with GROUP BY for aggregating related data.

STRING_AGG() is a powerful function that simplifies string concatenation in SQL Server, making queries cleaner and more efficient. Happy querying!

0
83

Related

Closing a SqlDataReader correctly prevents memory leaks, connection issues, and unclosed resources. Here’s the best way to do it.

Use 'using' to Auto-Close

Using using statements ensures SqlDataReader and SqlConnection are closed even if an exception occurs.

Example

using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    using (SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn))
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        while (reader.Read())
        {
            Console.WriteLine(reader["Username"]);
        }
    } // ✅ Auto-closes reader here
} // ✅ Auto-closes connection here

This approach auto-closes resources when done and it is cleaner and less error-prone than manual closing.

⚡ Alternative: Manually Close in finally Block

If you need explicit control, you can manually close it inside a finally block.

SqlDataReader? reader = null;
try
{
    using SqlConnection conn = new SqlConnection(connectionString);
    conn.Open();
    using SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
    reader = cmd.ExecuteReader();

    while (reader.Read())
    {
        Console.WriteLine(reader["Username"]);
    }
}
finally
{
    reader?.Close();  // ✅ Closes reader if it was opened
}

This is slightly more error prone if you forget to add a finally block. But might make sense when you need to handle the reader separately from the command or connection.

0
109

Measuring the execution time of C# methods is essential for performance optimization and identifying bottlenecks in your application.

The most straightforward approach uses the Stopwatch class from the System.Diagnostics namespace, which provides high-precision timing capabilities.

This approach is perfect for quick performance checks during development or when troubleshooting specific methods in production code.

Here's a practical example: Imagine you have a method that processes a large dataset and you want to measure its performance.

First, add using System.Diagnostics; to your imports. Then implement timing as shown below:

public void MeasurePerformance()
{
    Stopwatch stopwatch = new Stopwatch();
    
    // Start timing
    stopwatch.Start();
    
    // Call the method you want to measure
    ProcessLargeDataset();
    
    // Stop timing
    stopwatch.Stop();
    
    // Get the elapsed time
    Console.WriteLine($"Processing time: {stopwatch.ElapsedMilliseconds} ms");
    // Or use ElapsedTicks for higher precision
    Console.WriteLine($"Processing ticks: {stopwatch.ElapsedTicks}");
}

For more advanced scenarios, consider using the BenchmarkDotNet library, which offers comprehensive benchmarking with statistical analysis.

Simply install the NuGet package, decorate methods with the [Benchmark] attribute, and run BenchmarkRunner.Run<YourBenchmarkClass>() to generate detailed reports comparing different implementation strategies.

0
182

Reading a file line by line is useful when handling large files without loading everything into memory at once.

✅ Best Practice: Use File.ReadLines() which is more memory efficient.

Example

foreach (string line in File.ReadLines("file.txt"))
{
    Console.WriteLine(line);
}

Why use ReadLines()?

Reads one line at a time, reducing overall memory usage. Ideal for large files (e.g., logs, CSVs).

Alternative: Use StreamReader (More Control)

For scenarios where you need custom processing while reading the contents of the file:

using (StreamReader reader = new StreamReader("file.txt"))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

Why use StreamReader?

Lets you handle exceptions, encoding, and buffering. Supports custom processing (e.g., search for a keyword while reading).

When to Use ReadAllLines()? If you need all lines at once, use:

string[] lines = File.ReadAllLines("file.txt");

Caution: Loads the entire file into memory—avoid for large files!

3
258