-1

I am trying to write a query to return the users, some info about them and which security group they're assigned to. Some users have more than one group however, so I instead of returning 2 rows, I would like to combine the security group names.

SELECT  ur.loginname 
       ,ur.firstname 
       ,ur.lastname 
       ,sg.securitygroupname 
       ,jt.description 
FROM    users ur 
       ,usersecuritygroup us 
       ,securitygroup sg
       ,jobtitle jt 
WHERE  ur.doctorfacilityid = us.doctorfacilityid 
       AND us.securitygroupid = sg.securitygroupid 
       AND ur.jobtitle = jt.jtid 

So instead of getting 2 rows like this:

jdoe     john     doe     group1     xyz
jdoe     john     doe     group2     xyz

I would like to return 1 row like this:

jdoe     john     doe     group1, group2   xyz

The security group would be the only column that would have more than one row to meet the criteria (if that matters).

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • Hi and welcome to SO. Since you say you are new to sql I would strongly advise you to start using ANSI-92 style joins. They have been available now for more than 25 years. https://sqlblog.org/2009/10/08/bad-habits-to-kick-using-old-style-joins – Sean Lange Oct 04 '18 at 13:47
  • For the question at hand it has been asked and answered hundreds and hundreds of times. One of my favorite articles on the topic can be found [here](http://www.sqlservercentral.com/articles/comma+separated+list/71700/) – Sean Lange Oct 04 '18 at 13:49
  • The whole inner, outer, left, right etc joins confuse the heck out of me. So sorry - I'm a Business Systems Analyst so I'm sure my SQL needs won't be going beyond this. – iheartnjdevils Oct 04 '18 at 13:55
  • As for searching, I did try to search. I would much rather find the answer then have to wait for someone to maybe give me an answer. It looks like the keyword I was missing was "concatenate". As a novice, I look at the link you provided and I wouldn't even know how to use that to change my own query as it's way beyond me. I didn't realize the solution would be so elaborate (at least to me). – iheartnjdevils Oct 04 '18 at 13:57
  • Well what you are trying to do is force data from multiple rows into a single column. This is not how relational data works so the solution is not an easy one. – Sean Lange Oct 04 '18 at 13:59
  • As for joins it isn't that complicated. An inner join means give me only rows that match the join criteria. A left join is give me all the rows on the left side, then fill in the column value from the right side if there is a match (and multiple rows if there are multiple matches). Switch that around for right. This isn't a perfect illustration but gives you the basic concept. https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins – Sean Lange Oct 04 '18 at 14:01
  • Tab Alleman - You marked this as a duplicate - can you direct me to the answer that is sufficient for a novice sql user? – iheartnjdevils Oct 04 '18 at 14:07
  • I've read the differences but what is the left table? And when you join a second table, then what is the left table? Again, novice user here that just runs simple queries, usually to troubleshoot front end issues. – iheartnjdevils Oct 04 '18 at 14:09
  • The left table would be the first one in your query. select * from Table left join OtherTable on .... You decide which one is left with how you write your query. – Sean Lange Oct 04 '18 at 14:10
  • As for the duplicate, the marked answer is the correct one. As I said previously this is NOT simple stuff. It is complicated because you are forcing the database to return data in a way it is not intended to be used. – Sean Lange Oct 04 '18 at 14:11
  • @SeanLange The marked answer does not work for me. Before it was updated, it concatenated every single security group to every user and multiple times. Now, I can't even get it to run. – iheartnjdevils Oct 04 '18 at 14:45
  • Didn't mean to press enter.. I don't understand if I am supposed to run the first part of the answer and then the second part, or them together but neither works. When I run the first part, the error is, "Incorrect syntax near 'jtid'." If I run them together with the older version, I get "Incorrect syntax near the keyword 'select'". If I run older version by itself, I get error, "Invalid object name 'cte'." If I run them together with the newer version, I get the same select syntax error and if I run the new by itself, I get 'STRING_AGG' is not a recognized built-in function name. – iheartnjdevils Oct 04 '18 at 14:48
  • Well if STRING_AGG is a not a valid function you are probably on an older version of sql server. Thus you need to use STUFF and FOR XML – Sean Lange Oct 04 '18 at 15:19
  • @SeanLange I did try to use the older version, got it to execute but instead of just returning the users security group(s), it is returning 114 values (32 of them unique, the rest dupes). So I don't think the marked answer is entirely correct. – iheartnjdevils Oct 04 '18 at 15:54
  • If you want to eliminate duplicates just add distinct to your query. – Sean Lange Oct 04 '18 at 16:04

1 Answers1

0

You can put your query into a CTE and use XML to concatenate the values in securitygroupname column or if you are using SQL Server 2017 you can use STRING_AGG() function.

with cte as (
    SELECT  ur.loginname 
           ,ur.firstname 
           ,ur.lastname 
           ,sg.securitygroupname 
           ,jt.description 
    FROM    users ur 
           ,usersecuritygroup us 
           ,securitygroup sg
           ,jobtitle jt 
    WHERE  ur.doctorfacilityid = us.doctorfacilityid 
           AND us.securitygroupid = sg.securitygroupid 
           AND ur.jobtitle = jt.jtid
)
-- OLD VERSIONS
select distinct
    loginname
    ,firstname
    ,lastname
    ,STUFF(
        (SELECT
            N', ' + securitygroupname
        FROM cte c
        WHERE loginname = c.loginname
            and firstname = c.firstname
            and lastname = c.lastname
            and description = c.description
        FOR XML PATH(''), type).value('text()[1]', 'nvarchar(max)'), 1, 2, N''
    ) securitygroupname
    ,description
from cte

-- STARTING SQL SERVER 2017
select
    loginname
    ,firstname
    ,lastname
    ,STRING_AGG(securitygroupname, ',') as securitygroupname
    ,description
from cte
group by loginname, firstname, lastname, description
Valerica
  • 1,618
  • 1
  • 13
  • 20
  • 1
    If you're using SQL Server 2017+ you can use [String_Agg](https://learn.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017). – DavidP Oct 04 '18 at 13:51
  • Thanks mate, I was editing the answer when you commented :) – Valerica Oct 04 '18 at 13:53
  • Thanks! I'll see if I can figure that out. – iheartnjdevils Oct 04 '18 at 13:59
  • We have SQL server 2017 but get the error, 'STRING_AGG' is not a recognized built-in function name. – iheartnjdevils Oct 04 '18 at 14:06
  • @Valerica I have to use the older version but it is displaying security groups that user is not a part of as well as duplicate groups. For instance, I am part of 2 groups and that query returned 114 groups for me. Of those, 33 were duplicates of one group and 5 duplicates of the other group I'm in. The other 78 are (30 unique groups) I am not in. – iheartnjdevils Oct 04 '18 at 15:20
  • mate add more sample data to understand your problem, because for the data you have provided the above query is working alright. as for the string_agg problem i have no idea why it's not working properly for you; the only thing i have found is this link, maybe it will help you: https://blogs.msdn.microsoft.com/sqlmeditation/2018/03/05/functions-like-string_agg-concat_ws-trim-appear-to-break-in-visual-studio-2017-database-projects/ – Valerica Oct 04 '18 at 15:31