0

Consider this query and result set:

Select udbA.userId, d.dbName
from user_db_access udbA
Inner join dbList d on d.dbid = udbA.dbid 
Order By udbA.userId

1 az

1 nc_west

1 bsc_mo

1 NS_002

What I am looking for is a way to flatten this into one record. I know I can do it with a temp table and select into, but I was curious to see if a query could do it directly. A user could have up to 15 databases available to them.

Looking for results like below ( 2 columns -- userid and the database names ):

userid dbname

1 az nc_west bsc_mo NS_002

SQL Server Version: Microsoft SQL Server 2012 (SP3) (KB3072779) - 11.0.6020.0 (X64) Oct 20 2015 15:36:27 Copyright (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.3 (Build 9600: ) (Hypervisor)

Neo
  • 3,309
  • 7
  • 35
  • 44
  • 2
    How many items will you have per `userId`? Is it fixed, does it have a max or unlimited? – Charlieface Jan 06 '21 at 17:32
  • 1
    I added on my answer reference on how to achieve it in older versios: https://stackoverflow.com/questions/17591490/how-to-make-a-query-with-group-concat-in-sql-server – Jorge Campos Jan 06 '21 at 17:42

3 Answers3

2

Assuming you want a space-delimited list of database names:

DECLARE @Access table ( userId int, dbName varchar(50) );
INSERT INTO @Access VALUES
    ( 1, 'az' ), ( 1, 'nc_west' ), ( 1, 'bsc_mo' ), ( 1, 'NS_002' );

SELECT DISTINCT
    ax.userId, db.list
FROM @Access AS ax
OUTER APPLY (

    SELECT LTRIM ( (
        SELECT ' ' + dbName AS "text()" FROM @Access AS x WHERE x.userId = ax.userId
            FOR XML PATH ( '' )
    ) ) AS list

) AS db;

Returns

+--------+--------------------------+
| userId |           list           |
+--------+--------------------------+
|      1 | az nc_west bsc_mo NS_002 |
+--------+--------------------------+

For a comma-delimited list:

SELECT DISTINCT
    ax.userId, db.list
FROM @Access AS ax
OUTER APPLY (

    SELECT STUFF ( (
        SELECT ',' + dbName AS "text()" FROM @Access AS x WHERE x.userId = ax.userId
            FOR XML PATH ( '' )
    ), 1, 1, '' ) AS list

) AS db;

Returns

+--------+--------------------------+
| userId |           list           |
+--------+--------------------------+
|      1 | az,nc_west,bsc_mo,NS_002 |
+--------+--------------------------+
critical_error
  • 6,306
  • 3
  • 14
  • 16
1

You need GROUP BY and row_number as follows:

select userId,
       max(case when rn = 1 then dbName end) as val1,
       max(case when rn = 2 then dbName end) as val2,
       max(case when rn = 3 then dbName end) as val3,
       max(case when rn = 4 then dbName end) as val4
  from
(Select udbA.userId, d.dbName, 
        row_number() over (partition by udbA.userId order by d.dbName) as rn
   from user_db_access udbA
   Inner join dbList d on d.dbid = udbA.dbid ) t
group by userId

-- update

You just need two columns then use the STRING_AGG as follows:

Select udbA.userId, 
       string_agg(d.dbName, ' ') within group (order by d.dbName) as dbName 
  from user_db_access udbA
 Inner join dbList d on d.dbid = udbA.dbid
group by udbA,userId

-- For SQL server 2012

Select distinct udbA.userId, 
STUFF((SELECT distinct '' + d.dbName
         from dbList d
         where d.dbid = udbA.dbid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,0,'') AS dbid
FROM user_db_access udbA
WHERE EXISTS (SELECT 1 FROM dbList d
         where d.dbid = udbA.dbid)

Or use CTE as follows:

with CTE as
(Select udbA.userId, d.dbName
   from user_db_access udbA
   Inner join dbList d on d.dbid = udbA.dbid)
Select distinct c.userId, 
    STUFF((SELECT distinct '' + cc.dbName
             from cte cc
             where c.dbid = cc.dbid
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,0,'') AS dbid
    FROM cte c
Popeye
  • 35,427
  • 4
  • 10
  • 31
1

Depending on your SQL Server version you can use STRING_AGG

Like this:

Select udbA.userId, string_agg(d.dbName, ', ') as name
  from user_db_access udbA
        Inner join dbList d on d.dbid = udbA.dbid 
  group by udbA.userId

If you are using an older version you already have answers here: How to make a query with group_concat in sql server

Like this:

select
    udbA.userId,
    stuff((
        select cast(',' as varchar(max)) + d.dbName
          from dbList d
         where d.dbid = udbA.dbid
         order by d.dbName
       for xml path('')), 1, 1, '') as dbList
from
    user_db_access udbA 
      inner join dbList dl on dl.dbid = udbA.dbid 
order by
    udbA.userId;
Jorge Campos
  • 22,647
  • 7
  • 56
  • 87