2013-07-30 131 views
1

我在PHP中使用PDO的MySQL,我有一个SQL查询,它按预期工作。但是,我关心的表现,并想知道我是否可以改善我的查询。我还问,是因为我想增益SQL的一些背景知识两个SQL查询 - 性能差异?

比方说,我有一个有数等于领域(和一些其他信息,这是不同各表)两个表:

table `blog_comments`: id, userid (int) | timestamp (int) | content (varchar) | other 
table `projects_comments`: id, userid (int) | timestamp (int) | content (varchar) | other 

领域id主键userid + timestamp有两个表中的索引和时间戳是简单地用10(整数)长度unixtime。

举一个简单的垃圾邮件防护,我从提交新的评论阻止用户(无论博客,项目或其他任何东西),直至自他最后评论60秒已经过去了。要做到这一点,我得到的是用户从所有意见表的最新时间戳

这是我工作查询:

SELECT MAX(`last_timestamp`) AS `last_timestamp` 
FROM 
(
    SELECT `userid`, max(`timestamp`) AS `last_timestamp` 
    FROM `blog_comments` 
    GROUP BY `userid` 
    UNION ALL 
    SELECT `userid`, max(`timestamp`) as `last_timestamp` 
    FROM `projects_comments` 
    GROUP BY `userid` 
) AS `subquery` 
WHERE `userid` = 1 
LIMIT 0, 1; 

正如你可以看到,我用GROUP BY子查询内,并在主查询我只是过滤userid(在这种情况下:1)。优点:我只需要合格用户标识一次作为参数。

现在,我对SQL如何正确工作感兴趣。我认为这将是这样的:SQL首先由用户ID进行子查询,组所有现有行返回整个设置到主查询,然后应用where子句中找到所需要的用户ID。这对我来说似乎是一个巨大的性能泄漏。

所以我想略有更改查询

SELECT max(`last_timestamp`) AS `last_timestamp` 
FROM 
(
    SELECT max(`timestamp`) AS `last_timestamp` 
    FROM `blog_comments` 
    WHERE `userid` = 1 
    UNION ALL 
    SELECT max(`timestamp`) as `last_timestamp` 
    FROM `projects_comments` 
    WHERE `userid` = 1 
) AS `subquery` 
LIMIT 0, 1 

现在我必须通过用户ID两次了,还是一整套的行会抬头为给定的用户ID。我不知道这真的是改善的表现。

我没有任何大的数据量还没有真正测试它,也许我会做一些测试场景以后。我会真正有兴趣知道这些表中有多少数据集时是否会有所不同?

希望任何想法,信息和提示,在此先感谢。

编辑:第一查询

MySQL的讲解:第二查询

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 PRIMARY  <derived2> ALL  NULL NULL NULL NULL 4 Using where 
2 DERIVED  blog_comments range NULL userid 8 NULL 10 Using index for group-by 
3 UNION projects_comments index NULL userid 12 NULL 6 Using index 
NULL UNION RESULT <union2,3> ALL  NULL NULL NULL NULL NULL  

MySQL的解释:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 PRIMARY  <derived2> ALL  NULL NULL NULL NULL 2  
2 DERIVED  NULL NULL NULL NULL NULL NULL NULL Select tables optimized away 
3 UNION NULL NULL NULL NULL NULL NULL NULL Select tables optimized away 
NULL UNION RESULT <union2,3> ALL  NULL NULL NULL NULL NULL  

回答

2

回答你的问题第二个应该在MyS中表现更好QL比第一个,正是你给的原因。 MySQL将对所有数据运行完整的group by,然后然后选择一个组。

通过在查询前面放置一个explain可以看到不同的执行路径。这会给你一些关于查询真正在做什么的想法。

如果你有一个user_id, timestamp索引,那么第二个查询将运行得非常快,只使用索引。即使没有索引,第二个查询也会对这两个表进行全表扫描 - 就是这样。第一个将对聚合进行全表扫描和文件排序。第二个需要更长时间。

如果你想在userid传递只有一次,你可以这样做:

select coalesce(greatest(bc_last_timestamp, pc_last_timestamp), 
       bc_last_timestamp, pc_last_timestamp 
       ) 
from (select (SELECT max(`timestamp`) FROM `blog_comments` bc where bc.userid = const.userid 
      ) bc_last_timestamp, 
      (SELECT max(`timestamp`) FROM `projects_comments` pc where pc.userid = const.userid 
      ) pc_last_timestamp 
     from (select 1 as userid) const 
    ) t; 

查询看起来晦涩难懂,但它同样应该优化你的第二个。

+0

的确,查询看起来神秘嘿嘿,但我看到它是如何工作的。感谢您提醒我解释'功能。现在我看到,在第一个查询中,首先执行WHERE,这是否意味着GROUP BY将已经提到给定的userid? – Francodi

+0

@Francodi。 。 。不,我删除它。当我编辑代码时,这是代码碎片。 –

+0

对不起,我的意思是我提到的两个问题中的第一个。 'explain'告诉我主查询的WHERE在MySQL进入子查询之前执行,这就是为什么我会问'userid'是否已经在GROUP BY中考虑了? – Francodi

3

作为一个替代方法...

SELECT 'It''s been more than 1 minute since your last post' As result 
WHERE NOT EXISTS (
     SELECT * 
     FROM blog_comments 
     WHERE userid = 1 
     AND timestamp > Date_Sub(Current_Timestamp, INTERVAL 1 MINUTE) 
     ) 
AND NOT EXISTS (
     SELECT * 
     FROM projects_comments 
     WHERE userid = 1 
     AND timestamp > Date_Sub(Current_Timestamp, INTERVAL 1 MINUTE) 
     ) 

将会有一个结果,如果userid = 1还没有得到在两个表中的最后一分钟内的时间戳记录。

您也可以交换周围的逻辑...

SELECT 'You''re not allowed to post just yet...' As result 
WHERE EXISTS (
     SELECT * 
     FROM blog_comments 
     WHERE userid = 1 
     AND timestamp > Date_Sub(Current_Timestamp, INTERVAL 1 MINUTE) 
     ) 
OR  EXISTS (
     SELECT * 
     FROM projects_comments 
     WHERE userid = 1 
     AND timestamp > Date_Sub(Current_Timestamp, INTERVAL 1 MINUTE) 
     ) 

第二种选择可能会更有效(EXISTS VS NOT EXISTS),但是这对你进行测试和验证;)

+0

+1用于存在并用于在时间戳上打索引。但是,它不应该是时间戳> Date_Sub ...? –

+0

这是一个有趣且很好的解决方案,并且避免了'PHP'方面的任何进一步的调整。性能将等于我想我的问题中的第二个查询? – Francodi

+1

@GrahamGriffiths是的,它确实应该。修订。 – gvee