all groups > sql server programming > may 2005 >
You're in the

sql server programming

group:

concatenate string into one grouped rows?



concatenate string into one grouped rows? Ann
5/4/2005 9:05:25 PM
sql server programming: I have thousands of rows where data is like the following:


NAME ANIMAL SOUND DESC ACT EST
----- ------ ----- --------- --- ----
spot dogs 1d jump 0 0
spot dogs 1d bark 5 10
beauty horses 2c whine 10 10
beauty horses 2c 5 15
suki cats 1a meow 5 15
rover fox 44f howl 10 15
rover fox 52e bark 1 10
rover fox 52e cry 0 0


The user wants the data grouped by NAME and ANIMAL where SOUND and DESC
is concatenated for each instance of NAME and ANIMAL so the result
would be like that below. The ACT and EST are summed for each group.
All columns are varchar with the exception of the last two columns.
There can be nulls in any of the columns


NAME ANIMAL SOUND DESC ACT EST
----- ------ ----- --------- --- ----
spot dogs 1d jump bark 5 10
beauty horses 2c whine 20 25
sku cats 1a meow 5 15
rover fox 44f 52e howl bark cry 11 25


How would the code for this look to produce the results above?


Thanks in advance for your help.
Re: concatenate string into one grouped rows? Anith Sen
5/4/2005 11:46:01 PM
There is no built-in operators for sequential row concatenators in t-SQL.
Generally, if the number of values that are to be concatenated are a few,
you can write a series of CASE expressions inside an aggregate function with
a GROUP BY clause. For instance see:
http://groups.google.ca/group/microsoft.public.sqlserver.programming/msg/794aff53bfa47c1b
If the number of rows in the group are not knowm upfront, you will have to
look for some other alternatives:
http://groups.google.ca/group/microsoft.public.sqlserver.programming/msg/2d85bf366dd9e73e

But in any case, the recommended approach is to retrieve the resultset to
the client side application or report writer & massage the required
information in the desired format.

--
Anith

Re: concatenate string into one grouped rows? Steve Kass
5/5/2005 1:25:07 AM
Ann,

A cursor might be as fast as anything here. The solution below uses
a cursor for the aggregation, and also passes the string list through a
sorting function to alphabetize the values and eliminate duplicates. It's
only the sorting routine that needs the fixed-width fields for the SOUND
and DESC, so if you don't care about sorting and duplicates, you can
avoid all the padding and elminate much of the code.

Steve Kass
Drew University




create table T (
[NAME] varchar(20),
ANIMAL varchar(20),
SOUND varchar(10),
[DESC] varchar(10),
ACT int,
EST int
)

insert into T values('spot','dogs','1d','jump',0,0)
insert into T values('spot','dogs','1d','bark',5,10)
insert into T values('beauty','horses','2c','whine',10,10)
insert into T values('beauty','horses','2c','',5,15)
insert into T values('suki','cats','1a','meow',5,15)
insert into T values('rover','fox','44f','howl',10,15)
insert into T values('rover','fox','52e','bark',1,10)
insert into T values('rover','fox','52e','cry',0,0)
go

create function sk_merge(
@s_l varchar(1005),
@s_r varchar(1005)
) returns varchar(2000) as begin

declare @l int, @r int
set @l = datalength(@s_l) set @r = datalength(@s_r)
declare @ln varchar(10), @rn varchar(10)
declare @s_out varchar(2000)
set @s_out = ''
while @l*@r > 0 begin
set @ln = substring(@s_l,@l-9,10)
set @rn = substring(@s_r,@r-9,10)
set @s_out =
case
when @ln > @rn
then @ln
when @ln < @rn
then @rn
else '' end + @s_out
set @l = @l -
case
when @ln > @rn
then 10
else 0 end
set @r = @r -
case
when @ln > @rn
then 0
else 10 end
end
if @l > 0 set @s_out = left(@s_l,@l)+@s_out
if @r > 0 set @s_out = left(@s_r,@r)+@s_out
return @s_out
end
go

create function sk_mergesort(
@s_in varchar(2000)
) returns varchar(2000) as begin

declare @s_out varchar(2000)
return ''
end
go

alter function sk_mergesort(
@s_in varchar(2000)
) returns varchar(2000) as begin
declare @s_out varchar(2000)
if datalength(@s_in) <= 10
set @s_out = @s_in
else begin
declare @s_in_l varchar(1005)
declare @s_in_r varchar(1005)
declare @s_out_l varchar(1005)
declare @s_out_r varchar(1005)
declare @mid int
set @mid = datalength(@s_in)/10/2*10
set @s_in_l = substring(@s_in,1,@mid)
set @s_in_r = substring(@s_in,@mid+1,1005)
set @s_out_l = dbo.sk_mergesort(@s_in_l)
set @s_out_r = dbo.sk_mergesort(@s_in_r)
set @s_out = dbo.sk_merge(@s_out_l,@s_out_r)
end
return @s_out
end
go

declare @Result table (
[NAME] varchar(20),
ANIMAL varchar(20),
SOUND varchar(2000),
[DESC] varchar(2000),
ACT int,
EST int
)

DECLARE C CURSOR FAST_FORWARD FOR
SELECT [NAME], ANIMAL, SOUND, [DESC], ACT, EST
FROM T
ORDER BY [NAME]

OPEN C

BEGIN TRAN
DECLARE @currN varchar(20), @N varchar(20),
@nextS char(10), @S varchar(2000),
@nextD char(10), @D varchar(2000),
@A varchar(20), @ACT int, @EST int,
@ACTsum int, @ESTsum int,
@currA varchar(20)
FETCH NEXT FROM C
INTO @N, @A, @nextS, @nextD, @ACT, @EST
WHILE @@fetch_status = 0 BEGIN
SET @S = @nextS
SET @D = @nextD
SET @ACTsum = @ACT
SET @ESTsum = @EST
SET @currN = @N
SET @currA = @A
FETCH NEXT FROM C INTO @N, @A, @nextS, @nextD, @ACT, @EST
WHILE @@fetch_status = 0 AND @N = @currN BEGIN
SET @S = @S + @nextS
SET @D = @D + @nextD
SET @ACTsum = @ACTsum + @ACT
SET @ESTsum = @ESTsum + @EST
FETCH NEXT FROM C INTO @N, @A, @nextS, @nextD, @ACT, @EST
END
INSERT INTO @Result
SELECT @currN, @currA,
dbo.sk_mergesort(@S),
@D, @ACTsum, @ESTsum
END
COMMIT TRAN

CLOSE C
DEALLOCATE C

SELECT * FROM @Result
GO

DROP TABLE T
DROP FUNCTION sk_merge, sk_mergesort



[quoted text, click to view]
RE: concatenate string into one grouped rows? Lakshman
5/5/2005 6:04:03 AM
A similar case…

create table _data
(
Date datetime,
EmployeeId int,
Project varchar(50)
)

insert into _data values('20050503', 12345, 'VERIZON')
insert into _data values('20050503', 12345, 'CINGULAR')
insert into _data values('20050503', 12345, 'SPRINT')
insert into _data values('20050503', 24680, 'CINGULAR')

alter function dbo.Projects
(
@intEmployeeId int
)
returns varchar(8000)
as
begin
declare @vcProjects varchar(8000)

set @vcProjects = ''

select
@vcProjects = @vcProjects + case when @vcProjects = '' then '' else ', '
end + Project
from
_data (nolock)
where
EmployeeId = @intEmployeeId

return @vcProjects
end

select
Date, EmployeeId, dbo.Projects(EmployeeId) Projects
from
_data (nolock)
group by
Date, EmployeeId

results:

Date,EmployeeId,Projects
2005-05-03 00:00:00.000,12345,VERIZON, CINGULAR, SPRINT
2005-05-03 00:00:00.000,24680,CINGULAR

[quoted text, click to view]
Re: concatenate string into one grouped rows? Ann
5/5/2005 1:21:37 PM
Steve:

I tried what you suggested.

The only problem is that the result appears to only be grouping and
summing by the first field.

for lets say

NAME ANIMAL SOUND DESC ACT EST
----- ------ ----- --------- --- ----
rover fox 44f bark 5 5
rover fox 44f howl 0 10
rover fox 52e cry 5 10
rover chicken 22g peck 0 0

The result is showing up as:

NAME ANIMAL SOUND DESC ACT EST
----- ------ ----- --------- --- ----
rover fox 44f 52e 22g bark howl cry peck 10 25

(it's leaving out the chicken)

when it should be

NAME ANIMAL SOUND DESC ACT EST
----- ------ ----- --------- --- ----
rover fox 44f 52e bark howl cry 10 25
rover chicken 22g peck 0 0

Thanks again.

Ann
Re: concatenate string into one grouped rows? --CELKO--
5/5/2005 1:45:33 PM
Can explain what the "sound" column means? Also change DESC to
something like "sound_descropt" conform to ISO-11179 rules and to avoid
reserved words.

Next, you want to violate First Normal Form (1NF) in a database; why
are you smarter than 30 years of RDBMS theory? You do this kind of
display format in the front end of a tiered architecture. You can use
a cursor to get out of the relatioanl model and go back to sequential
file systems, if you wish. This is a kludge. But that is not the real
problem.

This destroys information which is a serious problem for me. When I
see a row like
('rover' , 'fox', '44f 52e', SET{bark, howl, cry}, 10, 25)

I have to ask how the totals are distributed; did the animal SET {bark
= 1, howl = 1, cry = 8 } for actual? or was it SET {bark = 8, howl = 1,
cry = 1} ? Very differrent information -- Am I beating my fox or
playing with him? There is a partition formula that will tell you how
much information you are destroying -- I can look it up, but Steve Kass
might know it off-hand. \

Why not group all sounds, since it is equally meaningless/meaningful at
that level of aggregation?

SELECT animal_name, species, SUM(actual_sound_cnt) AS actual_sound_tot,

SUM(estimated_sound_cnt) AS estimated_sound_tot
FROM FooBarZoo
GROUP BY animal_name, species;
Re: concatenate string into one grouped rows? Steve Kass
5/5/2005 4:37:25 PM
Ann,

Your original sample data did not have a [NAME]
with two different [ANIMAL] values, so I didn't
test an example like this.

Fortunately, I think it is a very simple fix:

-- What I posted:
WHILE @@fetch_status = 0 AND @N = @currN BEGIN

-- What will work:
WHILE @@fetch_status = 0 AND @N = @currN and @A = @currA BEGIN


SK


[quoted text, click to view]
Re: concatenate string into one grouped rows? Steve Kass
5/5/2005 5:02:28 PM
As usual, you're barking up the wrong tree as your usual
Pavlovian reflex. You seem to do this every time someone
wants a list of information.

Ann said "The user wants ..." and asked how "...to produce the results
above?"

She didn't say anywhere I saw that she is putting this into a database,
so just in case this isn't an unlearnable reflex, try to rest easy,
and call off the Normal Form police this time.

SK


[quoted text, click to view]
AddThis Social Bookmark Button