问题
Assume multiple vendors selling identical products. Each product has a number of possible colors. Finally, assume that the initial state of the database is that it is not aware of these products or its corresponding colors. The vendors will be adding product and color information.
The tables:
TABLE: vendor
================================
| vendor_id | name |
--------------------------------
| 1 | ABC Limited |
--------------------------------
| 2 | Acme Corporation |
--------------------------------
TABLE: product
=========================
| product_id | name |
-------------------------
| 1 | Widget 1 |
-------------------------
| 2 | Widget 2 |
-------------------------
TABLE: product_color_mapping
=========================
| color_id | product_id |
-------------------------
| 1 | 1 |
-------------------------
| 2 | 1 |
-------------------------
| 3 | 1 |
-------------------------
| 1 | 2 |
-------------------------
| 4 | 2 |
-------------------------
| 5 | 2 |
-------------------------
TABLE: color
=======================
| color_id | name |
-----------------------
| 1 | Red |
-----------------------
| 2 | White |
-----------------------
| 3 | Blue |
-----------------------
| 4 | Yellow |
-----------------------
| 5 | Green |
-----------------------
In order for color.name
to remain unique, product_color_mapping table is used to associate the product and color.
In the example above, Widget 1
can be either Red
, White
, or Blue
while Widget 2
can be either Red
, Yellow
, or Green
.
Issue 1:
I need a vendor_product
table to list the actual products a vendor is selling. How do I store in the database that ABC Limited
is selling a Red
Widget 1
? Would the table look like this:
TABLE: vendor_product
=====================================
| vendor_id | product_id | color_id |
-------------------------------------
| 1 | 1 | 1 |
-------------------------------------
The problem I have with this is that product_id
and color_id
are a composite key in product_color_mapping
table. I'm not sure what the proper way is to use a composite key as a foreign key.
Issue 2:
As stated, product and color information will be supplied by the vendors. Say vendor 1 has no products and enters its very first product: Widget 1
. Vendor 1 then assigns the product the color Red
.
The next time vendor 1 enters another product, I would like to provide
Widget 1
as a selectable item to vendor 1 (basically saying, "Hey vendor 1, you entered this product before. Is this the product you are trying to enter again?"). Vendor 1 can then either select the previously entered product OR enter a new product. If vendor 1 selectsWidget 1
, I then want to say "Hey vendor 1, you identified a previousWidget 1
as being colorRed
. Is this newWidget 1
alsoRed
?" -- from which vendor 1 can selectRed
or enter a new color.When vendor 2 comes along, how do I allow it to also add
Widget 1
to its inventory WITHOUT having a duplicateWidget 1
in the database?Finally, how do I identify
Widget 1
andRed
as being "valid" information -- and make it available to all vendors (not just vendor 1 who entered the information in the first place)?
NOTE: I'm not trying to find a solution for product and color. Products will have several attributes associated with it (e.g. sizes, such as "Small", "Medium", or "Large"). Color may also have several attributes associated with it - and those attributes may have attributes of their own and so on. The vendors will enter all these information.
回答1:
I need to be able to query the DB for values that a certain vendor has entered so that I can offer those as options. Somehow, the data must be associated with the vendor.
This is a fairly common issue to be solved in a variety of domains where multiple customers contribute to a common database structure but don't want to see each other's data. Oracle, for example, has something called a Virtual Private Database. In essence a column is added to each table and the value in the column for a given row indicates who "owns" the row. Views can be based on this:
CUSTOMERA : create view CUSTOMERAPRODUCTS as select * from products where products.user='CUSTOMERA'
CUSTOMERB: create view CUSTOMERBPRODUCTS as select * from products where products.user='CUSTOMERB'
You create composite keys (primary, foreign, and alternate unique) like this [pseudo-syntax]:
Table: COLORS
vendorid INT
colorid INT
color varchar(20)
PK = (vendorid, colorid)
UNIQUE index on (vendorid, color)
Table: PRODUCTS
vendorid INT
productid INT
product varchar(20)
PK = (vendorid, productid)
UNIQUE index on (vendorid, product)
Table: PRODUCTCOLORS
vendorid INT
productid INT
colorid INT
PK = (vendorid, productid)
UNIQUE index on (vendorid, color)
FK (vendorid, productid) references PRODUCTS(vendorid, productid)
FK (vendorid, colorid) references COLORS(vendorid, colorid)
Now, however, if you also wanted to make this particular rule (and similar) a requirement:
Color values must be unique not only within the individual vendor's subset
but unique system-wide (e.g. so that there is only one row containing 'Emerald Green')
You would have to do this in the COLORS table:
UNIQUE index on (color)
However, that would prevent vendor B from adding 'Emerald Green' to the COLORS table for use with their products if that particular color already existed in the table, yet vendor B could not see the color because that row would be filtered out of their views if a "virtual private database" or some analog of that approach was in effect,
So, if your goal is to have multiple data-tributaries flowing into a common data-river in which every vendor can swim freely, so to speak, then you have a potentially messy situation that typically requires tables like COLOR and PRODUCTCATEGORY to be maintained by a central administrator--centrally maintained because this situation usually results in data that looks like this:
Emerald Green
Emerald-Green
Emmerald Green
i.e. almost as many "variants" as there are tributaries, so your unique indexes become rather ineffectual. Isn't it pretty to think they're standing guard. You can have this variants problem with only one tributary! It's a challenge to keep these kinds of tables sane with only one person adding data! To eliminate such slop requires constant vigilance by a dedicated data administrator and far more sanitizing of batch imports than most companies are ever willing to engage in.
回答2:
That's a lot of questions.
I need a vendor_product table to list the actual products a vendor is selling. How do I store in the database that ABC Limited is selling a Red Widget 1? Would the table look like this:
If you want to deal only with id numbers, yes, it will look like that. But since vendor name, product name, and color name all have to be unique, you could also store the data like this.
vendor_name product_name color_name
--
ABC Limited Widget 1 Red
I'm not sure what the proper way is to use a composite key as a foreign key.
FOREIGN KEY (product_id, color_id)
REFERENCES product_color_mapping (product_id, color_id)
or
FOREIGN KEY (product_name, color_name)
REFERENCES product_color_mapping (product_name, color_name)
The next time vendor 1 enters another product, I would like to provide Widget 1 as a selectable item to vendor 1 (basically saying, "Hey vendor 1, you entered this product before.
This is a user-interface problem, not a database problem. But there are many ways to present existing data as choices.
- combo box (drop-down box)
- list box
- radio buttons
- multi-select buttons
- text box with intelligent completion
- new window (with any combination of the above)
- etc.
On the database side, the underlying query for any of those would probably be something like SELECT ... FROM vendor_product
. (Might use product_color_mapping or product instead.)
When vendor 2 comes along, how do I allow it to also add Widget 1 to its inventory WITHOUT having a duplicate Widget 1 in the database?
Product names have to be unique, right? So a second vendor can't add another row to the table "product" with the name of an existing product. This will probably be an ongoing problem for you, because vendors are liable to enter stuff like this.
Canon Powershot A800 10 MP Digital Camera with 3.3x Optical Zoom
Canon Powershot A800 10 M P Digital Camera with 3.3x Optical Zoom
Canon Powershot A800 10 MP Digital Camera with 3.3x Otpical Zoom
Canon Power Shot A800 10 MP Digital Camera with 3.3x Optical Zoom
Canon Powershot A-800 10 MP Digital Camera with 3.3x Optical Zoom
Canon Powershot A800 10 MP Digital Camera, 3.3x Optical Zoom
Canon Powershot A-800 10 MP Camera, 3.3x Opt. Zoom
Those are all unique. (As far as the database is concerned.) The challenge for the user-interface designer is to make product entry so easy to do right that vendors aren't inclined to create pseudo-duplicates.
Finally, how do I identify Widget 1 and Red as being "valid" information
Normally I'd say use a foreign key to the product_color_mapping table. But it seems like you're aiming to have a lot of different tables that might be the targets. "Widget 1" and "Red" we already know about, but in addition to that, you're also going to have "Widget 1" and "Small", and "Widget 1" and "Red", and "Widget 1" and "Deluxe".
回答3:
Another way to approach the problem is to use a non-relational or quasi-relational OODBMS. You throw normal form out the window with declarative referential integrity, and think of the data in class-inheritance terms. You would have a masters color table and each vendor would have its own COLORS table that 'extends' the master colors table. Your OOBMBS might enforce the rule:
colorname cannot be added to Vendor A's COLORS table if it already exists in MasterColors
or if it already exists in Vendor A's colors table
Then, when you go to describe a product with a color, your OODMBS would enforce this rule:
Product.ColorId IN (select colorid from master colors UNION select colorid from VendorAColors)
You'd have to make sure there were no collisions in the ids, of course. The IDs couldn't be simple autoincrementing integers. Each vendor might be assigned values in a certain range, or have a certain prefix, to distinguish their ids from each other's ids and from the master ids, or be a GUID.
You could implement this in any relational database of course, but it would mostly be a datastore; there would be very few opportunities to use declarative RI....(EDIT: unless, of course, your DBMS let you declare such constraints against reified views.)
来源:https://stackoverflow.com/questions/5521541/database-design-use-composite-key-as-fk-flag-data-for-sharing