BACKGROUND:I am running MS2005. I have a MASTER table (ID, MDESC) and a DETAIL table (MID, DID, DDESC) with data as follows
1 MASTER_1
2 MASTER_2
1 L1 DETAIL_M1_L1
1 L2 DETAIL_M1_L2
1 L3 DETAIL_M1_L3
2 L1 DETAIL_M2_L1
2 L2 DETAIL_M2_L2
If I join the tables with
SELECT M.*, D.DID FROM MASTER M INNER JOIN DETAIL D on M.ID = D.MID
I get a list like the following:
1 MASTER_1 L1
1 MASTER_1 L2
1 MASTER_1 L3
2 MASTER_2 L1
2 MASTER_2 L2
QUESTION: Is there any way to use a MS SQL select statement to get the detail records into a comma separated list like this:
1 MASTER_1 "L1, L2, L3"
2 MASTER_2 "L1, L2"
-
coalesce is your friend.
declare @CSL vachar(max) set @CSL = NULL select @CSL = coalesce(@CSL + ', ', '') + cast(DID as varchar(8)) from MASTER M INNER JOIN DETAIL D on M.ID = D.MID select @CSLThis will not work well for a generalized query (i.e. works great for a single master record).
You could drop this into a function... but that may not give you the performance you need/want.
Bill Karwin : That's clever, but it only works for one string at a time.cmsjr : He could wrap something like the above into a function that takes the id from the master table as an input, and returns the comma delimited string.Noah : Nice, but returns all on one line, like "L1, L2, L3, L1, L2" I need it grouped by the master rows -
This is the purpose of MySQL's
GROUP_CONCAT()aggregate function. Unfortunately, it's not very easy to duplicate this function in other RDBMS brands that don't support it.Noah : Thanks for the link, there was some new functionality added to SQL Server with the CROSS APPLY that I never noticed before. -
I think you need a Fucntion for this to work properly in recent version of SQL Server:
http://sqljunkies.com/WebLog/amachanic/archive/2004/11/10/5065.aspx?Pending=true
Noah : Thanks what I thought as well, but see the pure SQL solution posted -
You need a function:-
CREATE FUNCTION [dbo].[FN_DETAIL_LIST] ( @masterid int ) RETURNS varchar(8000) AS BEGIN DECLARE @dids varchar(8000) SELECT @dids = COALESCE(@dids + ', ', '') + DID FROM DETAIL WHERE MID = @masterid RETURN @dids ENDUsage:-
SELECT MASTERID, [dbo].[FN_DETAIL_LIST](MASTERID) [DIDS] FROM MASTERNoah : See posted solution without function ... very slickAnthonyWJones : This highlights why its good to specify versions in questions. APPLY is a SQL 2005 thing. SQL Servers tend to have a long life and hence in absence of version info I go with a SQL 2000 compatible answer.Noah : Nice point, I'll update the question to reflect this.CRice : Yes this is a solution I will use due to having SQL Server 2000, thanks -
Thanks to the concept in the link from Bill Karwin, it's the CROSS APPLY that makes it work
SELECT ID, DES, LEFT(DIDS, LEN(DIDS)-1) AS DIDS FROM MASTER M1 INNER JOIN DETAIL D on M1.ID = D.MID CROSS APPLY ( SELECT DID + ', ' FROM MASTER M2 INNER JOIN DETAIL D on M2.ID = D.MID WHERE M1.ID = M2.ID FOR XML PATH('') ) pre_trimmed (DIDS) GROUP BY ID, DES, DIDSRESULTS:
ID DES DIDS --- ---------- --------------- 1 MASTER_1 L1, L2, L3 2 MASTER_2 L1, L2AnthonyWJones : I agree it is quite slick, although the reliance on XML is a little distrubing but if works well enough without killing performance its a good solution.
0 comments:
Post a Comment