关于将键值对过滤为记录的MySQL优化

时间:2011-07-31 14:51:32

标签: mysql optimization subquery

我有一个数据库结构,旨在以易于扩展的方式存储特定对象的属性 有一个“对象”表。

+----+----------------------
| id | ....(name, type, etc)
+----+----------------------

接下来,我有一个“属性”表。

+----+------+
| id | Name |
+----+------+

最后,一个“关系”表,用于将所有数据作为属性 - 对象对(作为主键)与相应的值保持一致。

+--------+---------+-------+
| id_obj | id_attr | value |
+--------+---------+-------+

我需要同时为满足多个条件的对象获取ID。例如,我有名为“Type”和“City”的属性,我需要获取对象的ID,其中这些属性的对应值是“Apartment”和“City b”。

自昨天以来我一直碰到墙壁后设法提出的最佳解决方案(好吧,关于这个问题的唯一好处是它实际上可以工作并获取所需的记录):

SELECT objects.id
FROM (attributes INNER JOIN relations ON attributes.id = relations.id_attr) 
  INNER JOIN objects ON relations.id_obj = objects.id
WHERE objects.id
IN (
 SELECT objects.id
 FROM (attributes INNER JOIN relations ON attributes.id = relations.id_attr) 
  INNER JOIN objects ON relations.id_obj = objects.id
 WHERE attributes.name = 'Type' AND relations.value = 'Apartment'
)
AND objects.id
IN (
 SELECT objects.id
 FROM (attributes INNER JOIN relations ON attributes.id = relations.id_attr) 
  INNER JOIN objects ON relations.id_obj = objects.id
 WHERE attributes.name = 'City' AND relations.value = 'City b'
)
GROUP BY objects.id ASC
LIMIT 0 , 20

问题是,存储的数据量可能会变得有点大,我担心所有这些子查询(可能需要指定一些10-15个过滤器),每一个解析整个数据库,可能会导致严重性能问题(并不是说即使我的SQL技能有限,我也很确定必须有更好的方法来做我需要做的事情。) 另一方面,由于使用它的代码在很大程度上取决于当前的数据库结构,因此对数据库的重大改变并不是真正的选择。
有没有办法在单个查询中以我需要的方式检查属性,对存储的数据结构进行有限数量的更改或没有更改?


工作查询,相当于上面的一个但更好地优化,归功于DRapp

SELECT STRAIGHT_JOIN
      rel.id_obj
   from 
      relations rel
         join attributes atr
            on rel.id_attr = atr.id
   where 
         ( rel.value = 'Apartment' AND atr.name = 'Type'  )
      or ( rel.value = 'City b' AND atr.name = 'City' )
   group by 
      rel.id_obj
   having
      count(*) = 2
   limit
      0, 20

1 个答案:

答案 0 :(得分:4)

这应该可以得到你所需要的......每个“OR”d where子句条件,你可以继续作为一个合格的项目添加。然后,只需调整“Having”子句,以满足您允许的标准相同的数字...... 我把关系表放在第一位,因为在City的“Value”上有一个较小的匹配集或类型值...确保你在“VALUE”列的关系表上有一个索引。

SELECT STRAIGHT_JOIN
      rel.id_obj
   from 
      relations rel
         join attributes atr
            on rel.id_addr = atr.id
   where 
         ( rel.value = 'Apartment' AND atr.name = 'Type'  )
      or ( rel.value = 'Some City' AND atr.name = 'City' )
   group by 
      atr.id_obj
   having
      count(*) = 2
   limit
      0, 20

如果你想要所有实际的对象数据来自这个结果,你可以用它包装......

select obj.*
   from 
      ( complete SQL statement above ) PreQuery
         join Object obj on PreQuery.id_obj = obj.id