views:

165

answers:

4

I have the following parent <-> child datamodel:

(almost every line is a table, indented means child-of)

consumerGoods
    food
        meat
            item
        fruit
            item
        vegetable
            item

The child-items of meat, fruit and vegetables are in the same table (named items) because they have identical attributes. In the items table I have fields that describes the parent and the parentId.

So an item record could be:

id:1
parentType:meat
parentId:4
price:3.25
expDate:2009-12-31
description:bacon

I'm now building a full text MySQL search for the contents of the description field in "items", but I also want each result to have the information of its parent table, so a "bacon-item" has the data that's in its parent record. I also want each returned result to have data that is in the parent food record and the parent consumerGoods record.

I've got the following query now, but I don't know how to join based on the value of a field in a record, or if that's even possible.

SELECT * FROM item WHERE MATCH (description AGAINST ('searchKey')

One way to do this is is to do multiple queries for each matching "item" record, but if I had a lot of results that would be a lot of queries and would also slow down any filtering I'd want to do for facet-based searching. Another option is to make a new table that contains all the parent item info for each item record and search through that, but then I'd have to constantly update that table if I add item records, which is redundant and quite some work.

I'd like to hear it if I'm thinking in the right direction, or if I'm totally misguided. Any suggestions welcome.

+2  A: 

You could join against a subquery containing the union of all parent types:

select *
from item
left join (
    select 'meat' as type, Redness, '' as Ripeness from meat
    union all
    select 'fruit' as type, -1 as Redness, Ripeness from fruit
    union all
    select 'vegetable' as type, -1 as Redness, Ripeness from vegetable
) parent on parent.type = item.parentType

But if you can, redesign the database. Instead of the complex model, change it to one table of Items and one table of Categories. The categories should contain one row for meat, one for fruit, and one for vegetables.

Andomar
? What data fields (other than parentId and ParentType) could the UNION contribute to the overal query. And thence, what is the purpose of the join?
mjv
Good, this edit answered my first remark. The new query requires, however, to either "map" fields from each parent with one another or to declare many columns as empty.
mjv
@mjv: Edited to map fields, assuming that meat has "Redness" but fruits and vegetables have not
Andomar
Yes, Andomar, that's one approach I had in mind. Also why I asked Niels, the OP, about the `extent do the hierarchy` (to gage the possible complexity (read verbosity rather complexity) of a map declared at the level of the query text. Also, remember that Niels want to also include data from the next 2 levels-up (food, with I'm assumed different paths from say meat than fruit, and all the way back to Customeritems, with yet many more pathes...). At any rate, +1 for your solution, as this shows one possible approach. Niels will need to adapt to his particular reality.
mjv
@mjv: Here's to hoping the OP will ditch this design and start from scratch \*cheers\*
Andomar
@Andomar Yes! redesign, as hinted in your response is also an desirable option. Unfortunately one needs to ... compose... with legacy and its numerous couplings, and and redesign is not always in the cards. Let's hope for Niels sake that: either a redesign is ok, or, that the "ontology" of this model isn't too deep/complicated.
mjv
+2  A: 

As a general rule of thumb your database structure should contain data, but should not itself be data. A sign that you're breaking this is when you feel that you have to join to a different table based on the data you're reading from some other table. At that point you need to back up and consider your overall data model because odds are very good that you're doing something not quite right.

Donnie
+! Exactly right
Andomar
Good info, because that is the case in my datamodel right now.
Niels Bom
+1  A: 

As @Andomar suggested, the design is a bit off; having "multiple parent tables" does not map to DB foreign keys concept. Here is one possible suggestion. This one uses two levels of super-type/subtype relationships. Super-type table contains columns specific to all subtypes (categories), while subtype tables contain columns specific only to the category.



alt text

Damir Sudarevic
I couldn't come up with the right answer off the top of my head this morning, and I was going to try later on today. But this is the right answer! What's missing in your response is showing how simple, straightforward (and fast) all the the queries Niels wants are going to be. Mybe I'll add another answer that builds on yours.
Walter Mitty
+1  A: 

Since your example is contrived, it's difficult to know what the actual information requirements are in your case. Damir's diagram shows you the correct way to model PKs and FKs when you have a super-type sub-type relationships.

This situation is one case of a pattern called "generalization-specialization". Almost any treatment of object modeling will deal with generalization-specialization, although it may use different terminology. However, if you want to find articles that help you build a relational database that uses specialization-generalization, search for "generalization specialization relational modeling".

The best of the articles will start by teaching you the same concept that Damir's response illustrated for you. From there, you will learn how to create queries and views that can search for either all kinds of items, or for particular kinds of items, if you know what you are searching for.

A sample view follows:

create view FruitItems as
select
    c.ConsumerGoodsID,
    Price,
    Description,
    ConsumerGoodType,
    ExpiryDate,
    FoodType,
    IsTropic
from
    ConsumerGoods c
    INNER JOIN Food f on f.ConsumerGoodsID = c.ConsumerGoodsID
    INNER JOIN Fruit fr on fr.ConsumerGoodsID = c.ConsumerGoodsID

Similarly, you could create views for VegetableItems, MeatItems, and HouseSupplyItems, and even one large view, namely Items, that's the union of each of the specialized views.

In the Items view IsTropic would be true for all tropical fruits, false for all non tropical fruits, and null for Meats, Vegetables, and HouseSupplies. I'm not going to show you the entire Item view for a contrived case, but you get the idea. Especially if you read the best of the articles on relational modeling of this pattern.

The Items view might be a little slow, but it could come in handy when you really don't know any better way to search. And if you search for Istropic = True, you'll automatically exclude all the Meats, Vegetables, and HouseSupplies.

Walter Mitty