views:

5057

answers:

8

Hi,

I'm sorry if the question is long-winded and/or unclear. I will try and make it clearer with any feedback I get.

I've got two tables:

TableA

ID, Name

TableB

ID, SomeColumn, TableA_ID (FK for TableA)

The relationship is one row of TableA - many of TableB.

Now, I want to see a result like this:

ID     Name      SomeColumn

1.     ABC       X, Y, Z (these are three different rows)
2.     MNO       R, S

This won't work (multiple results in a subquery): SELECT ID, Name, (SELECT SomeColumn FROM TableB WHERE F_ID = TableA.ID) FROM TableA

This is a trivial problem if I do the processing on the client side. But this will mean I will have to run X queries on every page, where X is the number of results of TableA.

Note that I can't simply do a GROUP BY or something similar, as it will return multiple results for rows of TableA.

I'm not sure if a UDF, utilizing COALESCE or something similar might work?

+6  A: 

I think you are on the right track with COALESCE. See here for an example of building a comma-delimited string:

http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string

Ben Hoffstein
Awesome! I had seen some links discussing COALESCE, but they involved creating UDFs with triggers. The link you've submitted has the key, with a single SELECT statement.I'm adding an answer with the correct solution, so that it's easier for others to find. Thanks!
Wild Thing
Hi Ben,I think the answer needs a bit more detail, namely how to create the UDF, etc. Once I figure this out, I'll add the solution as a community editable answer. Please feel free to edit it, after which I'll accept that as the answer. Sorry for the confusion.
Wild Thing
A: 

You may need to provide some more details for a more precise response.

Since your dataset seems kind of narrow, you might consider just using a row per result and performing the post-processing at the client.

So if you are really looking to make the server do the work return a result set like

ID       Name       SomeColumn
1        ABC        X
1        ABC        Y
1        ABC        Z
2        MNO        R
2        MNO        S

which of course is a simple INNER JOIN on ID

Once you have the resultset back at the client, maintain a variable called CurrentName and use that as a trigger when to stop collecting SomeColumn into the useful thing you want it to do.

Bill
I thought of this, but wasn't very sure if this is an elegant solution - I'd like to have SQL Server return the properly constructed resultset, not something that'll need to be processed further. Would you require additional details? I've simplified the table structure, but I think you've got it.
Wild Thing
+3  A: 

In MySQL there is a group_concat function that will return what you're asking for.

SELECT TableA.ID, TableA.Name, group_concat(TableB.SomeColumn) 
as SomColumnGroup FROM TableA LEFT JOIN TableB ON 
TableB.TableA_ID = TableA.ID
Jacob
This would've been perfect, if there was a similar function in SQL Server. As it stands, I'm using Ben's solution to hammer together what I want.
Wild Thing
Sybase has also got LIST(). Come on Microsoft...
John Paul Jones
A: 

Assuming you only have WHERE clauses on table A create a stored procedure thus:

SELECT Id, Name From tableA WHERE ...

SELECT tableA.Id AS ParentId, Somecolumn 
FROM tableA INNER JOIN tableB on TableA.Id = TableB.F_Id 
WHERE ...

Then fill a DataSet ds with it. Then

ds.Relations.Add("foo", ds.Tables[0].Columns("Id"), ds.Tables[1].Columns("ParentId"));

Finally you can add a repeater in the page that puts the commas for every line

 <asp:DataList ID="Subcategories" DataKeyField="ParentCatId" 
DataSource='<%# Container.DataItem.CreateChildView("foo") %>' RepeatColumns="1"
 RepeatDirection="Horizontal" ItemStyle-HorizontalAlign="left" ItemStyle-VerticalAlign="top" 
runat="server" >

In this way you will do it client side but with only one query, passing minimal data between database and frontend

Sklivvz
+14  A: 

Figured it out finally! Ben, please feel free to either edit this into your answer, or to create a new answer with this content. I will then mark your answer as the solution.

1. Create the UDF:

    CREATE FUNCTION CombineValues
    (
    @F_ID INT --The foreign key from TableA which is used to fetch corresponding records
    )
    RETURNS VARCHAR(8000)
    AS
    BEGIN
    DECLARE @SomeColumnList VARCHAR(8000);

    SELECT @SomeColumnList = COALESCE(@SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20)) 
    FROM TableB C
    WHERE C.FK_ID = @FK_ID;

    RETURN 
    (
    SELECT @SomeColumnList
    )
    END

2. Use in subquery:

SELECT ID, Name, dbo.CombineValues(FK_ID) FROM TableA

Wild Thing
This still feels like a hack. I'm still using subqueries so, there's still a lot of extra processing going on. I'm sure a better solution exists (table restructuring, or another way of looking at the problem).
Wild Thing
I wouldn't call this a hack. It's more efficient than a cursor would be, and it lacks the overhead that would be necessary to create a temporary table with the data structured the way you want it.
Scott A. Lawrence
Shame the columns can't be parameters. As it stands you'll need to make a function for every child relationship!
John Paul Jones
That's ok - I need to combine only these particular columns. The rest are 'traditional' joins.
Wild Thing
+1  A: 

Typo in your query.

WHERE C.FK_ID = @FK_ID;

SB

WHERE C.FK_ID = @F_ID;

:)

A: 

Here the bellow Solution

SELECT GROUP_CONCAT(field_attr_best_weekday_value)as RAVI FROM content_field_attr_best_weekday LEFT JOIN content_type_attraction on content_field_attr_best_weekday.nid = content_type_attraction.nid GROUP By content_field_attr_best_weekday.nid

Use this, also you can change the Joins

Thanks [email protected]

ravi
+4  A: 

Even this will server the purpose

Sample data

declare @t table(id int, name varchar(20),somecolumn varchar(10))
insert into @t
    select 1,'ABC','X' union all
    select 1 ,'ABC','Y' union all
    select 1,'ABC','Z' union all
    select 2 ,'MNO','R' union all
    select 2 ,'MNO','S'

Query:

select ID,Name,
    stuff((select ',' + CAST(t2.SomeColumn as varchar(10))
     from @t t2 where t1.id = t2.id and t1.name = t2.name
     for xml path('')),1,1,'') SomeColumn
from @t t1
group by id,Name

Output:

ID  Name SomeColumn
1   ABC X,Y,Z
2   MNO R,S
priyanka.sarkar
Nt sure why this hasn't been picked up as it solves the problem without requiring a user function. You can see the same idea expressed here http://codecorner.galanter.net/2009/06/25/t-sql-string-aggregate-in-sql-server/ which predates this answer and so might be the "original"
Paul D'Ambra
Same here, not sure why this isn't rated higher
Marcel
Hi priyanka, can you tell me if and why the "and t1.name = t2.name" clause is necessary here?
Koen