2

我的系统中有以下(例如简化的)表:

查看 ER 图

目的是可以为给定类别指定功能,然后在子类别或产品级别覆盖。

有不同类型的要素具有不同的属性,这些类型由与要素具有 1:1 关系的“扩展”表表示。

例如,假设:

  • 所有电脑和灯都有交流适配器(类别)
  • 所有计算机都有 CPU(类别)
  • 所有笔记本电脑和平板电脑都有电池(子类别)
  • 所有 MacBook Pro 都有 HDMI 端口(产品)
  • 所有 MacBook Pro 和 iPad 都有 Retina 显示屏(产品)

这将被建模为:

分类:

  • 特征容器:ID:1
  • 类别:id:1,名称:“计算机”
  • 特征容器:ID:2
  • 类别:id:2,名称:“光”

子类别:

  • 特征容器:ID:3
  • 子类别:id:3,category_id:1,名称:“桌面”
  • 特征容器:ID:4
  • 子类别:id:4,category_id:1,名称:“笔记本电脑”
  • 特征容器:ID:5
  • 子类别:id:5,category_id:1,名称:“平板电脑”

  • 特征容器:ID:6

  • 子类别:id:6 category_id:2,名称:“地灯”
  • 特征容器:ID:7
  • 子类别:id:7,category_id:2,名称:“台灯”

产品:

  • 特征容器:ID:7
  • 产品:id:7,subcategory_id:4,名称:“Apple MacBook Air”
  • 特征容器:ID:8
  • 产品:id:8,subcategory_id:4,名称:“Apple MacBook Pro”
  • 特征容器:ID:9
  • 产品:id:9,subcategory_id:5,名称:“Apple iPad”

应用于类别的功能:

  • 特征:id:1,feature_container_id:1
  • has_ac_adapter_feature:id:1,model_number:“ABC”

  • 特征:id:2,feature_container_id:2

  • has_ac_adapter_feature:id:2,model_number:“DEF”

  • 特征:id:3,feature_container_id:1

  • has_cpu_feature:id:3,model_number:“123”

应用于子类别的功能:

  • 特征:id:4,feature_container_id:4
  • has_battery_feature:id:4,battery_type:“锂离子”
  • 特征:id:5,feature_container_id:5
  • has_battery_feature:id:5,battery_type:“锂离子”

应用于产品的特点:

  • 特征:id:6,feature_container_id:8
  • has_retina_display_feature: id:6
  • 特征:id:7,feature_container_id:9
  • has_retina_display_feature: id:7
  • 特征:id:8,feature_container_id:8
  • has_hdmi_port_feature: id:8

我不会费心为 Light 层次结构插入数据,但你明白了。请记住,我有:

  • 数以百万计的产品
  • 数以千计的子类别
  • 上百个类别
  • 几千个特征,主要定义在子类别上,但也有一些定义在产品和类别上。
  • 没有什么可说的,通常在类别级别(交流适配器)定义的功能实际上可能只在产品级别定义。这是一个业务呼叫,由灵活性与可维护性驱动。换句话说,我必须假设可以在任何级别分配任何功能。

我想查询以下内容:

给我所有具有 CPU 和 AC 适配器并具有(Retina 显示器或 HDMI 端口)的产品

我应该得到:

  • 苹果 MacBook Pro
  • 苹果iPad

这是我尝试过的...

我从这样的事情开始:

http://sqlfiddle.com/#!4/2f6cd/4/0

这试图处理继承问题,但后来我很快意识到,如果我开始过滤这个(产品有这个特性和那个特性),我不能轻易找到哪些产品有多个和/或特性(或者我可以?)。

然后我开始做这样的事情:

SELECT prd.id AS prd_id,
   CASE
       WHEN    prd_lvl.ac_ftr = 1
            OR cat_lvl.ac_ftr = 1
            OR scat_lvl.ac_ftr = 1
       THEN
           1
       ELSE
           0
   END
       AS ac_ftr
FROM product prd
   LEFT JOIN (SELECT prd.id AS prd_id,
                     prd.name AS prd_name,
                     NVL2 (ac_ftr.id, 1, 0) AS ac_ftr,
                     NVL2 (cpu_ftr.id, 1, 0) AS cpu_ftr,
                     NVL2 (bat_ftr.id, 1, 0) AS bat_ftr,
                     NVL2 (hdmi_ftr.id, 1, 0) AS hdmi_ftr,
                     NVL2 (ret_ftr.id, 1, 0) AS ret_ftr
                FROM feature_container fc
                     INNER JOIN product prd
                         ON fc.id = prd.id
                     INNER JOIN feature ftr
                         ON fc.id = ftr.feature_container_id
                     LEFT JOIN has_ac_adapter_feature ac_ftr
                         ON ftr.id = ac_ftr.id
                     LEFT JOIN has_cpu_feature cpu_ftr
                         ON ftr.id = cpu_ftr.id
                     LEFT JOIN has_battery_feature bat_ftr
                         ON ftr.id = bat_ftr.id
                     LEFT JOIN has_hdmi_port_feature hdmi_ftr
                         ON ftr.id = hdmi_ftr.id
                     LEFT JOIN has_retina_display_feature ret_ftr
                         ON ftr.id = ret_ftr.id) prd_lvl
       ON prd.id = prd_lvl.prd_id
   LEFT JOIN (      --All Products and subcategory-level-features assigned
              SELECT prd.id AS prd_id,
                     prd.name AS prd_name,
                     NVL2 (ac_ftr.id, 1, 0) AS ac_ftr,
                     NVL2 (cpu_ftr.id, 1, 0) AS cpu_ftr,
                     NVL2 (bat_ftr.id, 1, 0) AS bat_ftr,
                     NVL2 (hdmi_ftr.id, 1, 0) AS hdmi_ftr,
                     NVL2 (ret_ftr.id, 1, 0) AS ret_ftr
                FROM feature_container fc
                     INNER JOIN subcategory scat
                         ON fc.id = scat.id
                     INNER JOIN feature ftr
                         ON fc.id = ftr.feature_container_id
                     INNER JOIN product prd
                         ON scat.id = prd.subcategory_id
                     LEFT JOIN has_ac_adapter_feature ac_ftr
                         ON ftr.id = ac_ftr.id
                     LEFT JOIN has_cpu_feature cpu_ftr
                         ON ftr.id = cpu_ftr.id
                     LEFT JOIN has_battery_feature bat_ftr
                         ON ftr.id = bat_ftr.id
                     LEFT JOIN has_hdmi_port_feature hdmi_ftr
                         ON ftr.id = hdmi_ftr.id
                     LEFT JOIN has_retina_display_feature ret_ftr
                         ON ftr.id = ret_ftr.id) scat_lvl
       ON prd.id = scat_lvl.prd_id
   LEFT JOIN (         --All Products and category-level-features assigned
              SELECT prd.id AS prd_id,
                     prd.name AS prd_name,
                     NVL2 (ac_ftr.id, 1, 0) AS ac_ftr,
                     NVL2 (cpu_ftr.id, 1, 0) AS cpu_ftr,
                     NVL2 (bat_ftr.id, 1, 0) AS bat_ftr,
                     NVL2 (hdmi_ftr.id, 1, 0) AS hdmi_ftr,
                     NVL2 (ret_ftr.id, 1, 0) AS ret_ftr
                FROM feature_container fc
                     INNER JOIN category cat
                         ON fc.id = cat.id
                     INNER JOIN feature ftr
                         ON fc.id = ftr.feature_container_id
                     INNER JOIN subcategory scat
                         ON cat.id = scat.category_id
                     INNER JOIN product prd
                         ON scat.id = prd.subcategory_id
                     LEFT JOIN has_ac_adapter_feature ac_ftr
                         ON ftr.id = ac_ftr.id
                     LEFT JOIN has_cpu_feature cpu_ftr
                         ON ftr.id = cpu_ftr.id
                     LEFT JOIN has_battery_feature bat_ftr
                         ON ftr.id = bat_ftr.id
                     LEFT JOIN has_hdmi_port_feature hdmi_ftr
                         ON ftr.id = hdmi_ftr.id
                     LEFT JOIN has_retina_display_feature ret_ftr
                         ON ftr.id = ret_ftr.id) cat_lvl
       ON prd.id = cat_lvl.prd_id order by prd_id

但这有同样的问题:我不能在同一行得到东西。关于如何解决这个问题的任何建议?

谢谢!

4

1 回答 1

1

我认为你在这里建立的东西比EAV 模型更糟糕。只是,您没有将所有属性和值存储在一个表中,而是创建了大约一千个作为属性,而实际值很少。尽管这听起来很疯狂,但您可能想要更改为更多的 EAV 模型。查询会更容易,但不会太多。

数据模型的另一个大问题是feature表格。据我所知,它没有提供任何价值,需要在每个“实际”功能(如has_ac_adapter_feature)和feature_container. 您可以将每个“实际”功能表与一个feature表连接起来——每个表都需要一个。

你说你有几千个特征。我希望你明白你将不得不不断地构建动态查询,就好像你在一个 SQL 中包含所有“实际”特征表一样它太大了。

我已包含此 SQL 以演示您如何回答以下问题

给我所有具有 CPU 和 AC 适配器并具有(Retina 显示器或 HDMI 端口)的产品

我现在不在 Oracle DB 附近,所以语法可能有点偏差。对不起。

WITH 
    AC_ADAPTER_VIEW AS (
        SELECT feature.feature_container_id, ac_ftr.id
        FROM feature
            LEFT JOIN has_ac_adapter_feature ac_ftr
            ON ac_ftr.id = feature.id
    ),
    CPU_VIEW AS (
        SELECT feature.feature_container_id, cpu_ftr.id
        FROM feature
            LEFT JOIN has_cpu_feature cpu_ftr
            ON cpu_ftr.id = feature.id
    ),
    HDMI_VIEW AS (
        SELECT feature.feature_container_id, hdmi_ftr.id
        FROM feature
            LEFT JOIN has_hdmi_port_feature hdmi_ftr
            ON hdmi_ftr.id = feature.id
    ),
    RETINA_VIEW AS (
        SELECT feature.feature_container_id, ret_ftr.id
        FROM feature
            LEFT JOIN has_retina_display_feature ret_ftr
            ON ret_ftr.id = feature.id
    ),
    FEATURE_CONTAINER_VIEW AS (
        SELECT 
             fc.id as feature_container_id,
             ac_ftr.id AS ac_ftr,
             cpu_ftr.id AS cpu_ftr,
             bat_ftr.id AS bat_ftr,
             hdmi_ftr.id AS hdmi_ftr,
             ret_ftr.id AS ret_ftr
        FROM feature_container
             LEFT JOIN AC_ADAPTER_VIEW ac_ftr
                 ON feature_container.id = ac_ftr.feature_container_id
             LEFT JOIN CPU_VIEW cpu_ftr
                 ON feature_container.id = cpu_ftr.feature_container_id
             LEFT JOIN HDMI_VIEW hdmi_ftr
                 ON feature_container.id = hdmi_ftr.feature_container_id
             LEFT JOIN RETINA_VIEW ret_ftr
                 ON feature_container.id = ret_ftr.feature_container_id
    ),
    PRODUCT_VIEW AS (
        SELECT  product.id, FEATURE_CONTAINER_VIEW.*
        FROM    FEATURE_CONTAINER_VIEW
            INNER JOIN product
            ON product.id = FEATURE_CONTAINER_VIEW.feature_container_id
    ),
    SUBCATEGORY_VIEW AS (
        SELECT  product.id, FEATURE_CONTAINER_VIEW.*
        FROM    FEATURE_CONTAINER_VIEW
            INNER JOIN product
            ON product.subcategory_id = FEATURE_CONTAINER_VIEW.feature_container_id
    ), 
    CATEGORY_VIEW AS (
        SELECT  product.id, FEATURE_CONTAINER_VIEW.*
        FROM    FEATURE_CONTAINER_VIEW
            INNER JOIN subcategory
            ON subcategory.category_id = FEATURE_CONTAINER_VIEW.feature_container_id
            INNER JOIN product
            ON subcategory.id = product.subcategory_id
    )
SELECT
    product.*
FROM    
    product
    INNER JOIN PRODUCT_VIEW
        ON PRODUCT_VIEW.id = product.id
    INNER JOIN SUBCATEGORY_VIEW
        ON SUBCATEGORY_VIEW.id = product.id
    INNER JOIN CATEGORY_VIEW
        ON CATEGORY_VIEW.id = product.id
WHERE 
    --Check for CPU
    COALESCE( PRODUCT_VIEW.cpu_ftr, SUBCATEGORY_VIEW.cpu_ftr, CATEGORY_VIEW.cpu_ftr) is not null
    --Check for AC
    AND COALESCE( PRODUCT_VIEW.ac_ftr, SUBCATEGORY_VIEW.ac_ftr, CATEGORY_VIEW.ac_ftr) is not null
    --OR condition
    AND (
        --Check for HDMI
        COALESCE( PRODUCT_VIEW.hdmi_ftr, SUBCATEGORY_VIEW.hdmi_ftr, CATEGORY_VIEW.hdmi_ftr) is not null
        --Check for HDMI
        OR COALESCE( PRODUCT_VIEW.ret_ftr, SUBCATEGORY_VIEW.ret_ftr, CATEGORY_VIEW.ret_ftr) is not null
    )
于 2012-07-18T22:37:27.950 回答