in
Eric Vaillancourt on SQL Server...

MS SQL Dev

March 2008 - Posts

  • How to combine multiple rows of data on same line.

    I’m often ask how combine information on same line in a query.  Let me explain, lets say that you would like to list all products from the Production.product table in the AdventureWorks database and that you would like to list ALL vendors for each product from the Purchasing.Vendor table on the same line. 

    Look at the following example:

     

    clip_image002

     

    You can see in the 3rd column that all venders are list separated by a comma.

     

    In the AdventureWorks database the relation between the Product table and the Vendor table in done through a third table called ProductVendor.  Look at the following diagram:

    clip_image004

     

    Let’s start with this simple query that uses inner joins to link all three tables:

     

    SELECT Production.Product.Name AS [Product Name],

           Production.Product.ProductNumber,

           Purchasing.Vendor.Name AS Vendor

    FROM Production.Product INNER JOIN Purchasing.ProductVendor

         ON Production.Product.ProductID = Purchasing.ProductVendor.ProductID

    INNER JOIN Purchasing.Vendor

         ON Purchasing.ProductVendor.VendorID = Purchasing.Vendor.VendorID

    ORDER BY [Product Name]

     

    When you execute this query, you get the following:

     

    clip_image006

     

    In this example, we get all vendors on separate rows.  Not exactly what we want.  To solve this issue, we need to create a user defined function that will return all of our rows on a single line.  

    Execute the script below:

     

     

    CREATE FUNCTION [dbo].[ufnVendorOnSameLine](@Id int,@Delimiter varchar(10))

    RETURNS nvarchar(max)

    AS

    BEGIN

    DECLARE @OnSameLine varchar(max)

    set @OnSameLine=''

    --

    --by using SELECT to set the variable, all rows are concatenated together.

    SELECT @OnSameLine = @OnSameLine + coalesce (Purchasing.Vendor.Name,'')+@Delimiter

    FROM  Purchasing.ProductVendor INNER JOIN Purchasing.Vendor

          ON Purchasing.ProductVendor.VendorID = Purchasing.Vendor.VendorID

    WHERE (Purchasing.ProductVendor.ProductID = @Id)

    --

    --we need to remove the last delimiter

    set @OnSameLine = substring(@OnSameLine,1,len(@OnSameLine)-1)

    RETURN @OnSameLine

    END;

     

    Once we have created the udf, we need to modify the query to remove the Purchasing.Vendor.Name column and replace it with the function.  Also, we need to include a DISTINCT since we are unse an inner join to the Purchasing.ProductVendor table and finally, we remove the inner join to the Purchasing.Vendor table:

     

    SELECT DISTINCT Production.Product.Name AS [Product Name],

                    Production.Product.ProductNumber,

                    dbo.ufnVendorOnSameLine(ProductVendor.ProductID,', ') AS Vendors

    FROM Production.Product INNER JOIN Purchasing.ProductVendor

         ON Production.Product.ProductID = Purchasing.ProductVendor.ProductID

    ORDER BY [Product Name]

     

    How it Works:

     

    Basicaly, we use a technique that has been around since the early day of MS SQL Sever.  In fact prior to version 7, the SET command did not exist.  We needed to use the SELECT to assign a value to a variable. For example:

     

    SELECT @OnSameLine = @OnSameLine + coalesce (Purchasing.Vendor.Name,'')+@Delimiter

    and

    SET @OnSameLine = @OnSameLine + coalesce (Purchasing.Vendor.Name,'')+@Delimiter

    Are the same…it’s just a long forgotten technique.

     

    Hope this was useful,

     

    Eric Vaillancourt

     

©2008 SQLProf.com & Eric Vaillancourt
Powered by Community Server (Non-Commercial Edition), by Telligent Systems