【问题标题】:MySQL optimization on filtering key-value pairs as recordsMySQL优化将键值对过滤为记录
【发布时间】:2011-10-16 22:28:16
【问题描述】:

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

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

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

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

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

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

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

从昨天开始我的头撞到墙上后,我设法想出了最好的解决方案(好吧,这个查询的唯一好处是它确实有效并获取所需的记录):

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

【问题讨论】:

    标签: mysql optimization subquery


    【解决方案1】:

    这应该可以满足您的需求...每个“OR”d where 子句条件,您都可以继续添加为合格项目。然后,只需调整“Having”子句以满足与您允许的标准相同的数量...... 我已将关系表放在首位,因为这将在 City 的“值”或类型值上具有较小的匹配集...确保您在“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
    

    【讨论】:

    • 谢谢!我知道我必须让所有记录至少满足一个标准,然后以某种方式只获得满足所有标准的记录,但我不确定如何准确地做到这一点。通过将较小的“属性”表中的数据连接到较大的表中,STRAIGHT_JOIN 选项是否使 MySQL 更有效地工作?
    • 我在原始问题中添加了适用于我的数据库的查询版本。
    • @Havelock,很抱歉没有早点回复。 STRAIGHT_JOIN 有帮助,但您确实需要知道查询中的数据和最佳前导表,以获得您想要的最小结果......例如不要使用链接到数百万条记录的表的查找表。寻找更小/能够索引元素的标准。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-17
    • 2012-09-19
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多