父级(默认 0)
使用 NULL 而不是 0。这将允许您使用正确的 FOREIGN KEY(如下图所示)。
我应该为每个产品分配类别和子类别 ID
你不应该。只需分配适用于该产品的最具体的子类别。所有超类别都可以从CATEGORY
表中定义的层次结构中推断出来,因此将它们(甚至其中之一)存储在产品中是多余的。
出于性能原因,有时可以证明这种冗余是合理的,但是您必须在它们与数据一致性之间取得平衡。在这种情况下,没有很好的声明方式来保持冗余数据与“主”数据同步——你必须通过触发器或(上帝保佑)应用程序代码来做到这一点,这样做可能会引入并发错误,除非您非常非常仔细地考虑您的锁定策略。
然后基于每个产品属于一个类别的假设......
这种假设使您的模型不正确。要正确建模这种 N:1 关系,您不需要联结表PRODUCT_TO_CATEGORY
,您只需要产品和类别之间的外键:
注意:PRODUCT.CATEGORY_ID
如果您想允许无类别产品(即您的关系实际上是 N:0..1 而不是 N:1),请使 NULL-able。
你的数据看起来像:
CATEGORY:
1 TV NULL
2 PLASMA 1
3 LED 1
PRODUCT:
1 Sony 1 (TV)
2 Sharp 2 (PLASMA)
3 Samsung 2 (PLASMA)
如何检索当前和每个子类别的产品?
首先识别属于所需“子树”的所有类别,然后检索连接到它们的产品。在这种情况下,搜索所有电视可以这样完成:
获取您正在搜索的“顶级”类别的 ID:1(电视)。
SELECT CATEGORY_ID FROM CATEGORY WHERE TITLE = 'TV'
获取所有子类别的 ID:2 (PLASMA) 和 3 (LED)。
SELECT CATEGORY_ID FROM CATEGORY WHERE PARENT_ID IN (1)
重复步骤 (2),直到到达层次结构的“底部”:
SELECT CATEGORY_ID FROM CATEGORY WHERE PARENT_ID IN (2, 3)
上面的查询将为您的示例数据返回一个空结果集,因此您知道可以停止。在更精细的层次结构中,您可能需要多次重复上述查询,始终将 IN 列表替换为最近检索到的类别“层”。
如果您的数据碰巧有一个循环,请注意无限迭代!
将所有产品连接到任何已识别的类别:1 (TV)、2 (PLASMA) 和 3 (LED)。
SELECT * FROM PRODUCT WHERE CATEGORY_ID IN (1, 2, 3)
不幸的是,MySQL 不支持那种允许您在单个数据库往返中完成所有这些的递归查询。