是否有可能以更快的方式执行此SELECT查询?

时间:2011-02-12 16:16:44

标签: sql mysql database query-optimization

更新(根据每个人的回复):

我正在考虑更改我的结构,以便我有一个名为prx_tags_sportsitems的新表。我将完全删除prx_lists。 prx_tags_sportsitems将作为ID表的引用来替换prx_lists.ListString,它曾经存储属于每个prx_sportsitem的标签的ID。

新关系将如此:

  • prx_tags_sportsitems.TagID< - > prx_tags.ID
  • prx_sportsitems.ID< - > prx_tags_sportsitems.OwnerID

prx_tags将包含TagName。这样我仍然可以将每个“标签”维护为一个单独的独特实体。

我查找所有标有“有氧”标签的sportsitems的新查询将类似于以下内容:

SELECT prx_sportsitems.* FROM prx_sportsitems, prx_tags_sportsitems
WHERE prx_tags_sportsitems.OwnerID = prx_sportsitems.ID 
AND prx_tags_sportsitems.TagID = (SELECT ID FROM prx_tags WHERE TagName = 'aerobic')
ORDER BY prx_sportsitems.DateAdded DESC LIMIT 0,30;

或许我可以用“IN”条款做点什么,但我还不确定。

在我对脚本进行这么大的修改之前,每个人都赞成吗?评论?非常感谢!

原始帖子:

说到MYSQL查询,我宁愿是新手。当我最初设计我的数据库时,我做了一些相当愚蠢的事情,因为它是我能找到的唯一解决方案。现在我发现它似乎对我的MYSQL服务器造成了太大的压力,因为执行每个查询需要0.2秒,我相信如果它是一个更好的查询可能更像0.02秒(或表设计,如果它来吧!)。我想避免需要重建我的整个网站结构,因为它的设计方式与目前的方式非常相似,所以我希望有更快的mysql查询。

我的数据库中有三个表:

  1. 体育项目表
  2. 标签表
  3. 列表
  4. 每个体育项目都分配了多个标签名称(类别)。每个“标签”都作为单独的结果存储在prx_tags中。我在prx_sportsitems中的体育项目的prx_lists中创建一个“列表”,并通过链接到prx_sportsitems.ID的prx_lists.OwnerID链接它们

    这是我当前的查询(查找所有标记为“有氧”的体育项目):

    SELECT  prx_sportsitems.* 
    FROM    prx_sportsitems, prx_lists 
    WHERE   prx_lists.ListString LIKE (CONCAT('%',(SELECT prx_tags.ID 
                                                   FROM prx_tags
                                                   WHERE prx_tags.TagName = 'aerobic'
                                                   limit 0,1),'#%')) 
     AND    prx_lists.ListType = 'Tags-SportsItems' 
     AND    prx_lists.OwnerID = prx_sportsitems.ID
     ORDER BY prx_sportsitems.DateAdded
     DESC LIMIT 0,30
    

    为了帮助澄清更多信息,包含所有标记ID的列表位于名为ListString的单个字段中,我将其结构如下:“#1#2#3#4#5”......从中,上面的查询“concats”prx_tags.ID哪个标记名是'有氧'。

    我的想法是,可能没有更快的查询存在,我需要简单地接受我需要做一些更简单的事情,例如将所有标签放在列表中,直接放在prx_sportsitems中的一个名为“new”的新字段中TagsList“然后我可以简单地运行一个查询,它可以从prx_sportsitems中选择*,其中TagsList LIKE'%aerobic%' - 但是,我想避免重新设计我的整个网站。我真的后悔没有事先考虑优化:(

4 个答案:

答案 0 :(得分:7)

每当我写一个查询,并且认为我需要使用LIKE时,我脑子里就会发出一个警报,表示可能有更好的设计。这肯定是这种情况。

您需要重新设计prx_lists表。从你所说的,很难说出确切的架构应该是什么,但这是我最好的猜测:

prx_lists应该有三列:OwnerIDListTypeTagName。然后,对于OwnerID具有的每个标记,您将拥有一行。您的上述查询现在看起来像这样:

SELECT prx_sportsitems.*
FROM prx_sportsitems, prx_lists
where prx_lists.TagName = 'aerobic'
      AND prx_lists.OwnerID = prx_sportsitems.ID

这是一个更有效的查询。也许ListType也不属于该表,但如果没有关于该列用于什么的更多信息,很难说。

不要忘记create the appropriate indexes!这将提高性能。

重构数据库架构可能会很痛苦,但在我看来,这是解决长期问题的唯一方法。

答案 1 :(得分:1)

  

为了帮助澄清更多,列表即   包含所有标记ID在里面   一个名为ListString和I的字段   结构它是这样的:“#1#2#3#4#5”......从那里,上面的查询“concats”prx_tags.ID   标记名是'有氧'。

那里有你的问题。不要将分隔数据存储在DB字段(ListString)中。以这种方式建模数据将极难/不可能针对它编写高性能查询。

建议:将ListString的内容分解为相关的表格,每个项目都有一行。

答案 2 :(得分:1)

  1. 如果没有,请不要进行任何更改 看着execution plan。 (和 也可以通过编辑你的帖子来发布 原始问题。)
  2. 你的LIKE子句的方式 构造,MySQL不能使用 指数。
  3. LIKE子句是一种症状。您的 表结构更有可能成为问题。
  4. 通过建立理智的表格,你可能会获得至少一个数量级的改善。

      

    我真的后悔没有调查   预先优化

    这不是导致你的问题的原因。不了解数据库设计的基础知识会导致您的问题。 (这是一个观察,而不是批评。你可以解决无知。你无法修复愚蠢。)

    <强>后来

    发布您的existing table structure和您提议的更改。我们能够预测您的代码将会做什么,而不是能够预测您对代码的描述会做什么,您会更高兴。

答案 3 :(得分:1)

  

包含所有标记ID的列表位于名为ListString的单个字段中,我将其结构如下:“#1#2#3#4#5”......从中,上面的查询“concats “prx_tags.ID这个标记名是'有氧'。

不仅存储非规范化数据,而且分隔符不常见。

临时改进

改进方法的最快方法是将当前使用的分隔符(“#”)更改为逗号:

UPDATE PRX_LISTS
   SET liststring = REPLACE(liststring, '#', ',')

然后,您可以使用MySQL's FIND_IN_SET function

  SELECT si.* 
    FROM PRX_SPORTSITEMS si
    JOIN PRX_LISTS l ON l.ownerid = si.id
    JOIN PRX_TAGS t ON FIND_IN_SET(t.id, l.liststring) > 0 
   WHERE t.tagname = 'aerobic'
     AND l.listtype = 'Tags-SportsItems' 
ORDER BY si.DateAdded DESC 
   LIMIT 0, 30

长期解决方案

正如您所经历的那样,在非规范化数据中搜索细节效果不佳,并使查询过于复杂。您需要更改PRX_LISTS表,以便一行包含SPORTSITEM.owneridPRX_TAGS.id的唯一组合,以及您可能需要的任何其他列。我也建议重命名 - 究竟是什么?这个名字太通用了:

CREATE TABLE SPORTSITEM_TAGS_XREF (
   sportsitem_ownerid INT,
   tag_id INT,
   PRIMARY KEY (sportsitem_ownerid INT, tag_id)
)