1

I have some tables in SQL Server 2005:

  • Product: ID, Name
  • Category: ID, Name
  • Tags: ID, tagName
  • ProductCategory: productId, tagId
  • CategoryTags: categoryId, tagId

Basically I need a query that will display the category and list of tags associated for each product on ONE line.

For example, there are 3 categories: Animals, Countries, Color.

Each one of those have a bunch of tags.

Lets say that Product 1 has a bunch of tags associated such as: bird, duck, dog, canada, russia, japan, black, red, white

I need the query result to be in the format:

productId, [Category:tag,tag,tag;Category:tag,tag,tag:Category:tag,tag,tag]

1, [Animal:bird,duck,dog;Country:canada,russia,japan;Color:black,red,white]

The text in the square brackets should be in one column returned by SQL.

I found something similar here: Concatenate many rows into a single text string?

But I need to take it a step further and have it all on one line instead of it returning the different categories on separate rows.

Kinda hard to explain but hope you get it.

Is this possible?

Thanks in advance.

UPDATE: Thanks for everyone's help and input. Really appreciate it! Here is what I have so far which is close, but not quite there yet. Perhaps it'll help you figure it out for me :D

http://sqlfiddle.com/#!3/eed14/5

Community
  • 1
  • 1
yaki33
  • 65
  • 2
  • 8
  • 3
    what rdbms are you using? – Taryn Jan 07 '13 at 20:03
  • take a look at: http://stackoverflow.com/questions/451415/simulating-group-concat-mysql-function-in-microsoft-sql-server-2005 – JeffS Jan 07 '13 at 21:31
  • It's similar to what I need except instead of just having 1 category with the list of tags in the result, I need all the categories associated to the product with the list of tags. I guess if you use the example in the link you posted. I would need the result to say: ANDY:A100,B391,X010;TOM:A100,A510 all in one line. – yaki33 Jan 07 '13 at 21:40

3 Answers3

1

SQL Fiddle

MS SQL Server 2008 Schema Setup:

create table Product
(
  ID int, 
  Name varchar(10)
)

create table Category
(
  ID int, 
  Name varchar(10)
)

create table Tags
(
  ID int, 
  Name varchar(10)
)

create table ProductCategory
(
  productId int,
  categoryId int
)

create table CategoryTags
(
  categoryId int, 
  tagId int
)

insert into Product values(1, 'Product 1')

insert into Category values(1, 'Animals')
insert into Category values(2, 'Countries')
insert into Category values(3, 'Color')

insert into Tags values(1, 'Bird') 
insert into Tags values(2, 'Duck') 
insert into Tags values(3, 'Dog') 
insert into Tags values(4, 'Candada') 
insert into Tags values(5, 'Russia') 
insert into Tags values(6, 'Japan') 
insert into Tags values(7, 'Black') 
insert into Tags values(8, 'Red') 
insert into Tags values(9, 'White') 

insert into ProductCategory values(1, 1)
insert into ProductCategory values(1, 2)
insert into ProductCategory values(1, 3)

insert into CategoryTags values(1, 1)
insert into CategoryTags values(1, 2)
insert into CategoryTags values(1, 3)
insert into CategoryTags values(2, 4)
insert into CategoryTags values(2, 5)
insert into CategoryTags values(2, 6)
insert into CategoryTags values(3, 7)
insert into CategoryTags values(3, 8)
insert into CategoryTags values(3, 9)

Query 1:

select P.ID,
       P.Name,
       (
       select ';'+C.Name+':'+
              (
              select ','+T.Name
              from CategoryTags as CT
                inner join Tags as T
                  on CT.tagId = T.ID
              where CT.categoryId = C.ID
              for xml path(''), type
              ).value('substring(text()[1], 2)', 'varchar(max)') 
       from ProductCategory as PC
         inner join Category as C
           on PC.categoryId = C.ID
       where PC.productId = P.ID
       for xml path(''), type
       ).value('substring(text()[1], 2)', 'varchar(max)') as ColumnName
from Product as P

Results:

| ID |      NAME |                                                                 COLUMNNAME |
-----------------------------------------------------------------------------------------------
|  1 | Product 1 | Animals:Bird,Duck,Dog;Countries:Candada,Russia,Japan;Color:Black,Red,White |
Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
  • I changed the field `tagid` in `ProductCategory` to `categoryId` so that it is a junction table between Product and Category. – Mikael Eriksson Jan 07 '13 at 22:17
  • The result is exactly what I'm looking for. However, in your example, you are assigning entire categories and all their associated tags to a product. I need to be able to assign specific tags to products such as: Dog, Canada, Black, White. Not all the tags associated to the category. – yaki33 Jan 07 '13 at 22:29
  • The query I have here is almost there. Just need to group it by Category instead of having the Category repeating. http://sqlfiddle.com/#!3/eed14/5 – yaki33 Jan 07 '13 at 22:39
0

Just to give a sample try this please: Well this is done in MYSQL with it's easiest group_concat function...though. Coz you haven't mentioned which RDBMS exactly you are using.

select x.productid, group_concat(c.name), 
group_concat(x.tags)
from (
select p.productid, ct.categoryid,
group_concat(t.tagname) as tags
from productcategory p
left join tags t
on t.id = p.tagid
left join  
categorytags ct
on t.id = ct.tagid
group by p.productid, ct.categoryid) x
left join category c
on c.id = x.categoryid
group by x.productid
;

Results:

PRODUCTID   GROUP_CONCAT(C.NAME)    GROUP_CONCAT(X.TAGS)
1       cat1,cat2                   tag2,tag1,tag1
2       cat1                        tag2

SQLFIDDLE DEMO

bonCodigo
  • 14,268
  • 1
  • 48
  • 91
  • Please take a look and comment. Wish you coule provide some sample data though or you can update the above sqlfiddle link by adding your correct table `create/insert` commands :) – bonCodigo Jan 07 '13 at 20:31
  • Sorry I didn't specify, I'm using MSSQL2005. – yaki33 Jan 07 '13 at 21:06
  • The result should look like: cat1:tag1,tag2;cat2:tag1,tag3,tag4;cat3:tag3,tag5; – yaki33 Jan 07 '13 at 21:28
  • Then it gets more interesting as you do not have `group_concat` in MS SQL server. ;) It's much better if you had given a try to do create and insert commands in the fiddle link... – bonCodigo Jan 07 '13 at 21:40
  • Haha yes it does. Plus it seems like I would somehow have to group by category for each product and for each category group by tag – yaki33 Jan 07 '13 at 21:43
0

This is a really ugly way to get this data into a single string. But you can use something like this:

select distinct 
  p.name ProductName,
  STUFF((SELECT distinct ', ' + 
           c.name +':'+ STUFF((SELECT DISTINCT ', ' + Name 
                               FROM tags t
                               LEFT JOIN CategoryTags ct
                                  on t.id = ct.tagid
                               WHERE c.id = ct.categoryid
                               FOR XML PATH('')), 1, 1, '')
         FROM category c 
         LEFT JOIN CategoryTags ct
           ON ct.categoryid = c.id
         LEFT JOIN productcategory pc
           ON pc.tagid = ct.tagid
         WHERE p.id = pc.productid
         FOR XML PATH('')), 1, 1, '')  List
from product p

See SQL Fiddle with Demo

The result is:

| PRODUCTNAME |                                                                               LIST |
----------------------------------------------------------------------------------------------------
|        Test |  Animal: Bird, dog, duck, Color: black, red, white, Country: canada, japan, russia |

Edit #1: This is producing the result that you want:

select distinct 
  p.name ProductName,
  STUFF((SELECT distinct '; ' + 
           c.name +':'+ STUFF((SELECT DISTINCT ', ' + Name 
                               FROM catalogTags t
                               LEFT JOIN catalogProductTags pt
                                 on t.id = pt.catalogTagId
                               LEFT JOIN catalogCategoryTags ct
                                 on pt.catalogTagId = ct.catalogTagId
                               WHERE c.id = ct.catalogCategoryId
                                  AND p.id = pt.catalogProductId
                               FOR XML PATH('')), 1, 1, '')
         FROM catalogProductTags pt
         LEFT JOIN catalogCategoryTags ct
           ON pt.catalogTagId = ct.catalogTagId
         LEFT JOIN catalogCategory c 
           ON ct.catalogCategoryId = c.id
         WHERE p.id = pt.catalogProductId
         FOR XML PATH('')), 1, 1, '')  List
from catalogProduct p;

See SQL Fiddle with Demo. This can probably be refactored to a cleaner version.

The result is:

| PRODUCTNAME |                                                        LIST |
-----------------------------------------------------------------------------
|     tshirt1 |                           Animals: dog; Color: black, white |
|     tshirt2 |              Animals: dog; Color: blue, red; Countries: USA |
|     tshirt3 |  Color: blue, pink, red, white; Countries: Australia, Japan |
Taryn
  • 242,637
  • 56
  • 362
  • 405
  • Again, this is close. Except that you are assigning entire categories to products just like Mikael Eriksson's suggestion instead of specific tags. I need to be able to tag products with specific tags such as: Dog, Canada, Black, White and not the entire set of tags associated to a category. – yaki33 Jan 07 '13 at 22:37
  • @user1956062 can you edit your OP with some sample data and then the desired result? It would be easier to see what you want for the final product. – Taryn Jan 07 '13 at 22:38
  • The query I have here is almost there. Just need to group it by Category instead of having the Category repeating. I need the format to be just like your result format http://sqlfiddle.com/#!3/eed14/5 – yaki33 Jan 07 '13 at 22:40
  • @user1956062 please see my edit. The new version produces the result that you want. – Taryn Jan 08 '13 at 00:25
  • Seems like the results you listed is correct. For some reason when I go to the SQL Fiddle link it says that "datasource[3_eed14] doesn't exist. I rebuilt the schema and ran the query. Don't know if it's only happening to me. UPDATE: Nevermind, it's working now – yaki33 Jan 08 '13 at 00:37
  • @user1956062 Happy to help. It kind of hurt my brain too, but it was fun to solve. :) – Taryn Jan 08 '13 at 00:40