May 25, 2006 at 8:44 am
Hi All,
This task sounded easy when I was first given it - I appear to be wrong!
I have a table structure like this...
id category name allnames
------ ---------------- -------------- ------------------------
1 fruit apple
2 fruit pear
3 fruit orange
4 meat beef
5 meat pork
6 meat gammon
What I need is a way to update the allnames column so that it is like this..
id category name allnames
------ ---------------- -------------- ------------------------
1 fruit apple apple, pear, orange
2 fruit pear apple, pear, orange
3 fruit orange apple, pear, orange
4 meat beef beef, pork, gammon
5 meat pork beef, pork, gammon
6 meat gammon beef, pork, gammon
I have found some code which will select out the values from the first table in a csv string into a variable, but can't find a way to update them back into the table.
I could use a cursor of course, but there are many rows in this table, and a cursor takes far too long and is, as we all know, far too inefficient.
Any ideas?
Many thanks,
Martin
May 25, 2006 at 9:03 am
using your fruittable as a model, maybe something like this will help?
you'd still need to use a cursor, but just one cursor for each category; the code for within the cursor is bold
create table #fruittable(
id int identity(1,1) primary key,
category varchar(30),
name varchar(30),
allnames varchar(200) )
insert into #fruittable(category,name) values('fruit','apple')
insert into #fruittable(category,name) values('fruit','pear')
insert into #fruittable(category,name) values('fruit','orange')
insert into #fruittable(category,name) values('meat','beef')
insert into #fruittable(category,name) values('meat','pork')
insert into #fruittable(category,name) values('meat','gammon')
declare @fruitlist varchar(200)
set @fruitlist=''
select @fruitlist=@fruitlist + isnull(name,'') + ',' from #fruittable where category='fruit'
print @fruitlist
update #fruittable set allnames=@fruitlist where category='fruit'
select * from #fruittable
set @fruitlist=''
select @fruitlist=@fruitlist + isnull(name,'') + ',' from #fruittable where category='meat'
print @fruitlist
update #fruittable set allnames=@fruitlist where category='meat'
select * from #fruittable
you'd need to strip the last comma, but this is pretty close:
results:
1 | fruit | apple | apple,pear,orange, |
2 | fruit | pear | apple,pear,orange, |
3 | fruit | orange | apple,pear,orange, |
4 | meat | beef | beef,pork,gammon, |
5 | meat | pork | beef,pork,gammon, |
6 | meat | gammon | beef,pork,gammon, |
Lowell
May 25, 2006 at 9:17 am
You dont need to use a cursor here...
This solution does rely on the data being ordered by category in the test table but you can always preorder it. This should be much faster than a cursor.
------------DDL----------------
create table test (id int identity, category varchar(100),
name varchar(100), allnames varchar(8000) null)
insert test (category, name)
select 'fruit', 'apple' union
select 'fruit', 'pear' union
select 'fruit', 'orange' union
select 'meat' , 'beef' union
select 'meat' , 'pork' union
select 'meat' , 'gammon'
------------------------------------
declare @category varchar(100), @name varchar(100)
select @name ='' ,@category =category from test where id = 1
update t
set @name = case when @category = category then @name +','+name else name end , allnames = @name, @category = category
from test t
update test
set allnames = x.allnames
from test t
join (select max(allnames)allnames, category from test group by category)x
on x.category = t.category
----------------------------------------------
May 25, 2006 at 9:22 am
--As A cursor solution:
declare
@fruitlist varchar(2000),
@categ varchar(64)
declare c1 cursor for select distinct category from #fruittable
open c1
fetch next from c1 into @categ
While @@fetch_status <> -1
begin
set @fruitlist=''
select @fruitlist=@fruitlist + isnull(name,'') + ',' from #fruittable where category=@categ
if len(@fruitlist) > 1
--trim trailing
set @fruitlist = substring(@fruitlist,1,len(@fruitlist) -1)
update #fruittable set allnames=@fruitlist where category=@categ
fetch next from c1 into @categ
end
close c1
deallocate c1
select * from #fruittable
Lowell
May 25, 2006 at 2:02 pm
Thanks very much for your replies everybody. One thing my simplistic example didn't make very obvious is that there could be up to 40,000 rows in the table, so using a cursor would be a last resort. Jules, your solution is now in production and doing a damn fine job!!
Thanks again
May 25, 2006 at 4:53 pm
May 25, 2006 at 11:24 pm
Very clever.
Slight mod for you...Take out the initial variable load and you won't get the leading (or trailing) commas... change the two variable lengths also gets rid of the long record error.
------------DDL----------------
create table test (id int identity, category varchar(100),
name varchar(100), allnames varchar(8000) null)
insert test (category, name)
select 'fruit', 'apple' union
select 'fruit', 'pear' union
select 'fruit', 'orange' union
select 'meat' , 'beef' union
select 'meat' , 'pork' union
select 'meat' , 'gammon'
------------------------------------
declare @category varchar(20), @name varchar(20)
--select @name ='' ,@category =category from test where id = 1
update t
set @name = case when @category = category then @name +','+name else name end , allnames = @name, @category = category
from test t
update test
set allnames = x.allnames
from test t
join (select max(allnames)allnames, category from test group by category)x
on x.category = t.category
I'd also suggest that you might want to do this in a separate table so you don't have quite as much denormalization... of course, denormalized data is nasty fast.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 26, 2006 at 2:27 am
Even better - Thanks, Jeff.
This is definately one for my code snippets library
May 26, 2006 at 5:32 am
I certainly wouldn’t recommend storing the data in this format, as you correctly point out it isn’t even in 1st normal form.(3rd is the minimum i would recommend). However i assume this formatting is done on the fly within a stored procedure and stored in temp table.
The only reason for doing this is for performance and, as i am sure you know, the relational model does sometimes have to be adapted to cope with the practical limitations of current technology. Running a procedural loop through 40,000 rows on puny client machines for example may not acceptable.
Having said that I will always encourage you brown shirts of the relational model to enforce conformance to the only solid theoretical foundation available to us.
In this instance though my idealistic friend, you are preaching to the converted.
May 26, 2006 at 9:13 pm
I don't think he intends to store that in his database, Joe. 3rd party vendors frequently as for data in such a form. And, no, there's not always a front end application available to do it for you especially if it's an ad-hoc request.
Or are you just a new, bad SQL programmer looking for some proprietary kludge to avoid the effort to learn to do it right? |
There's a lot of good ways to say that what someone is doing might be wrong... I'm thinking the method above isn't one of them...
--Jeff Moden
Change is inevitable... Change for the better is not.
May 30, 2006 at 4:19 am
Hi again,
apologies for being neglecting my post - I have been away for the weekend.
you are correct, Jeff and Jules. This data is held in a 'cache' table. The user uses a thin client (intranet) to select criteria to display summary financial information back to his web browser. The browser displays url's for each summary figure so that the user can 'drill down' into the data (user clicks on url to drill into, another call is made to the .net web application which will retrieve data from the database). This data drill down has to be very fast, which is the reason for storing this data temporarily in this manner. The data is deleted when the users session ends.
Joe, I guess this is a real world example of Lowell's brilliant signature above - "In Theory, theory and practice are the same...In practice, they are not" - I have been a sql programmer for several years, and am well aware of the basic fundamentals behind normalization, but sometimes there are valid reasons for not following them. Make sure you have the facts before being openly critical!
Martin
Viewing 11 posts - 1 through 10 (of 10 total)
You must be logged in to reply to this topic. Login to reply