Listing results in a single column

  • Hi guys . . . I'm guessing that this has been asked before, but I'm going to ask again, anyway.

    I have three tables: two data tables (in the following example, I'll call them #Names and #Offices) and a junction table (#Index). I've set up a sample here for your enjoyment:

    create table #Names (NameID int primary key, Name varchar(50))

    create table #Offices (OffID int primary key, Loc varchar(50))

    create table #Index (NameID int, OffID int)

    insert into #Names (NameID, Name) values (1, 'Derek Jeter')

    insert into #Names (NameID, Name) values (2, 'Hawkeye Pierce')

    insert into #Names (NameID, Name) values (3, 'Steve McGarrett')

    insert into #Offices (OffID, Loc) values (1, 'New York')

    insert into #Offices (OffID, Loc) values (2, 'Crabapple Cove')

    insert into #Offices (OffID, Loc) values (3, 'Honolulu')

    insert into #Index (NameID, OffID) values (1, 1)

    insert into #Index (NameID, OffID) values (2, 2)

    insert into #Index (NameID, OffID) values (3, 3)

    insert into #Index (NameID, OffID) values (2, 1)

    insert into #Index (NameID, OffID) values (2, 3)

    insert into #Index (NameID, OffID) values (3, 1)

    Here's what I want to do: I want to return the office IDs from the #Index junction table as a single column, not as a table join.

    In other words, I'm looking for this (note: commas and order are optional):

    NameID Name OffID

    1 Derek Jeter 1

    2 Hawkeye Pierce 1, 2, 3

    3 Steve McGarrett 1, 3

    I don't want to do a JOIN, like this:

    NameID Name OffID

    1 Derek Jeter 1

    2 Hawkeye Pierce 1

    2 Hawkeye Pierce 2

    2 Hawkeye Pierce 3

    3 Steve McGarrett 1

    3 Steve McGarrett 3

    I'm at a loss as to how to do this. Can anyone offer any suggestions?

    Thanks!

    +--------------------------------------------------------------------------------------+
    Check out my blog at https://pianorayk.wordpress.com/

  • Here's a sample concat code. Enjoy the little tweaks!

    SELECT

    T.name,

    STUFF((

    SELECT

    ',' + name

    FROM

    sys.columns C

    WHERE C.object_id = T.object_id

    ORDER BY

    name

    FOR

    XML PATH('')

    ) , 1 , 1 , '') As Columns_

    FROM sys.tables T

    ORDER BY name

  • Man, you're not kidding about the tweaks!

    I got the jist of it, nevertheless. Thanks!

    +--------------------------------------------------------------------------------------+
    Check out my blog at https://pianorayk.wordpress.com/

  • with CTE as

    (

    select name,nameid

    from #names

    )

    SELECT

    name,

    offidlist = STUFF((

    SELECT

    ',' + cast(offId as varchar(3))

    FROM #index

    where nameid = cte.nameid

    order by offid asc

    FOR

    XML PATH('')

    ) , 1 , 1 , '')

    FROM cte

    Only because you talk baseball with me.

    ______________________________________________________________________________________________
    Forum posting etiquette.[/url] Get your answers faster.

  • Ray K (6/28/2011)


    Man, you're not kidding about the tweaks!

    I got the jist of it, nevertheless. Thanks!

    This concept is a litter harder to comprehend so I can't just give you the ready made answer!

    Tho it was just a matter of changing 2 table names and adding a join + convert. :hehe:

  • calvo (6/28/2011)


    with CTE as

    (

    select name,nameid

    from #names

    )

    SELECT

    name,

    offidlist = STUFF((

    SELECT

    ',' + cast(offId as varchar(3))

    FROM #index

    where nameid = cte.nameid

    order by offid asc

    FOR

    XML PATH('')

    ) , 1 , 1 , '')

    FROM cte

    Only because you talk baseball with me.

    And I'm thankful . . . even if you are a Red Sox fan! 😀

    +--------------------------------------------------------------------------------------+
    Check out my blog at https://pianorayk.wordpress.com/

  • Ninja's_RGR'us (6/28/2011)


    Here's a sample concat code. Enjoy the little tweaks!

    SELECT

    T.name,

    STUFF((

    SELECT

    ',' + name

    FROM

    sys.columns C

    WHERE C.object_id = T.object_id

    ORDER BY

    name

    FOR

    XML PATH('')

    ) , 1 , 1 , '') As Columns_

    FROM sys.tables T

    ORDER BY name

    Thanks Ninja's_RGR'us. I've been using COALESCE for this sort of thing, but this is more straght-forward in a way - I like it. "I like it a lot."

    The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge. - Stephen Hawking

  • Welcome all, in the name of whoever I stole it from on these forums :hehe:.

    Sorry but I don't know the originator so I'll just give credits to Bill Gates. :alien::smooooth:

  • lawlz

    ______________________________________________________________________________________________
    Forum posting etiquette.[/url] Get your answers faster.

  • Here is one other option that seems a bit more intuitive to me but that requires the use of SQLCLR.

    This:

    with CTE as

    (

    select name,nameid

    from #names

    )

    SELECT

    name,

    offidlist = STUFF((

    SELECT

    ',' + cast(offId as varchar(3))

    FROM #index

    where nameid = cte.nameid

    order by offid asc

    FOR

    XML PATH('')

    ) , 1 , 1 , '')

    FROM cte

    Can be written as this:

    SELECT n.Name,

    dbo.GROUP_CONCAT(i.OffID) AS offidlist

    FROM #Index i

    JOIN #Names n ON i.NameID = n.NameID

    GROUP BY n.Name

    No Visual Studio required either. You can get the compiled SQLCLR User-defined Aggregate here and deploy it using only T-SQL: http://groupconcat.codeplex.com/[/url]

    There are no special teachers of virtue, because virtue is taught by the whole community.
    --Plato

  • How much faster is it?

  • Ninja's_RGR'us (6/28/2011)


    How much faster is it?

    Depending on the use case it can be pretty significant, but it depends on the use case and the data of course. The UDA outperformed the "XML PATH, TYPE" method in 4 out of 6 use cases. If you include the "XML PATH" method (which suffers from escape issues) the UDA only outperforms the XML in 3 out of 6 use cases. My tests were done on sets of 300K and 600K rows.

    Edit: pretty significant means ~10% faster

    There are no special teachers of virtue, because virtue is taught by the whole community.
    --Plato

  • Okay, here's a recursive CTE version should other methods not work for you. It does have it's own problems however (such as recursion limit) and I suspect the performance would be pretty poor. But I include it here as another example even though I'd likely try the XML method first.

    with

    ConcatList as

    (

    select distinct

    0 as RowCounter

    , NameID

    , Name

    , cast('' as varchar(max)) as Listed

    , 0 as LastOffID

    from #Names

    union all

    select RowCounter + 1

    , cl.NameID

    , cl.Name

    , cl.Listed + ',' + cast(min(ii.OffID) over (partition by cl.NameID) as varchar(max))

    , min(ii.OffID) over (partition by cl.NameID)

    from ConcatList cl

    inner join #Index ii

    on ii.NameID = cl.NameID

    where ii.OffID > LastOffID

    )

    , CleanedList as

    (

    select RowCounter

    , NameID

    , Name

    , stuff(Listed, 1, 1, '') as OffID

    , max(RowCounter) over (partition by NameID) as LastRowCounter

    from ConcatList

    )

    select distinct

    NameID

    , Name

    , OffID

    from CleanedList

    where RowCounter = LastRowCounter

    Steve.

  • Why the heck are you going recursive with this???

  • Ninja's_RGR'us (6/28/2011)


    Why the heck are you going recursive with this???

    Hey, don't get me wrong, I agree with your sentiments exactly. Any form or recursion/looping should be avoided like the plague.

    That said, it is still an option albeit one from the bottom of the barrel. The OP may face constraints that prevent the use of better options, and a poor option is better than no option.

    Steve.

Viewing 15 posts - 1 through 15 (of 22 total)

You must be logged in to reply to this topic. Login to reply