如何在没有多个联接的情况下从标准化数据库结构中检索值?

时间:2018-12-19 21:28:46

标签: mysql

我有下面的db shema(MySQL 5.x),在其中我将项目的值保存在一个表中,并将每个属性的对应名称保存在另一个表中:

classified_attr(属于特定机密的属性)

cl_id | value
39393 | 173

cat_attr(属性名称):

attr_id | attr_de
173       green
123       available

这适用于我需要的所有情况,但我遇到了一些性能问题,因为我需要通过一个SQL查询从一项中检索所有值(包括名称)。

我通过为每个值多次联接表来做到这一点。例如

SELECT 
    ca_power.value AS power_id,
    catr_power.attr_de AS power,   
    ca_avail.value AS avail_id,  
    catr_avail.attr_de AS avail                              
FROM
    classifieds AS c
LEFT JOIN classifieds_attr AS ca_power ON c.ID = ca_power.cl_id AND ca_power.attr_group_id = 19
LEFT JOIN cat_attr AS catr_power ON ca_power.attr_group_id = ca_power.attr_group_id AND catr_power.attr_id =  ca_power.value
LEFT JOIN classifieds_attr AS ca_avail ON c.ID = ca_avail.cl_id AND ca_avail.attr_group_id = 17
LEFT JOIN cat_attr AS catr_avail ON ca_avail.attr_group_id = ca_avail.attr_group_id AND catr_avail.attr_id =  ca_avail.value

对于这两个属性及其名称,可以忽略不计,但是现在有10多个属性,我正面临性能下降的问题。

是否有一种方法可以更改我的SQL查询,以便在保持结构的同时以更快的方式检索包括名称在内的所有这些值?如果没有,那么存储这些值的更好方法是什么?

2 个答案:

答案 0 :(得分:1)

好吧,您的查询有点奇怪,因为您多次将classifieds_attr表自身连接到自身(ca_power.attr_group_id = ca_power.attr_group_idca_avail.attr_group_id = ca_avail.attr_group_id),这完全没有必要,特别是因为它是作为一部分完成的cat_attr表的联接条件。

此外,您对classifieds_attrcat_attr表的多个联接似乎仅在attr_group_id列上有所不同(在上面的规范中不存在)。

您可以通过删除多余的联接并将其变成透视查询来简化查询:

SELECT 
    c.id as classified_id,
    -- Power Group (19)
    max(case when ca.attr_group_id = 19 then ca.value end) AS power_id,
    max(case when ca.attr_group_id = 19 then catr.attr_de end) AS power,
    -- Avail Group (17)
    max(case when ca.attr_group_id = 17 then ca.value end) AS avail_id,
    max(case when ca.attr_group_id = 17 then catr.attr_de end) AS avail
FROM
    classifieds AS c
LEFT JOIN classifieds_attr AS ca
  ON c.ID = ca.cl_id
 AND ca.attr_group_id in (17,19)
LEFT JOIN cat_attr AS catr
  ON catr.attr_id =  ca.value
GROUP BY c.id

要添加其他列(组),只需复制汇总列(注意上面的注释列),更改case语句中的组ID,然后将适当的组ID添加到第一个联接的in列表中。

您还可以尝试将cat_attrclassifieds_attr进行内部联接,假设ca.value是必填字段,并且是cat_attr引用attr_id的外键。您没有提供太多的元数据,因此很难知道您的设置。无论如何,这就是潜在的优化:

SELECT c.id as classified_id,
    -- Power Group (19)
       max(case when ca.attr_group_id = 19 then ca.value end) AS power_id,
       max(case when ca.attr_group_id = 19 then catr.attr_de end) AS power,
    -- Avail Group (17)
       max(case when ca.attr_group_id = 17 then ca.value end) AS avail_id,
       max(case when ca.attr_group_id = 17 then catr.attr_de end) AS avail
  FROM classifieds AS c
  LEFT JOIN classifieds_attr AS ca
       JOIN cat_attr AS catr
         ON catr.attr_id =  ca.value
    ON c.ID = ca.cl_id
   AND ca.attr_group_id in (17,19)
 GROUP BY c.id

如果您不希望看到没有属性的classified.id,那么还可以将第一个联接从外部联接切换为内部联接,并可能获得一些额外的性能。

答案 1 :(得分:0)

我将添加以下索引(如果您还没有的话):

create index ix1 on classifieds_attr (cl_id, attr_group_id);

create index ix2 on cat_attr (attr_id);

这将大大加快您的查询速度。不过,由于查询有两个多余的,无用的过滤条件,因此查询中有些麻烦。

您似乎有多个单列索引。但是,第一个索引-综合索引-对于性能至关重要。