SELECT FROM Subquery性能

时间:2013-03-07 22:33:22

标签: php mysql sql

我正在为我的项目寻找更多性能(PHP + MySQL),有一个看起来太慢的查询(从1个表中选择2个随机用户)

id  | name |  total | img
------------------------ --
1    user1   500      1
2    user2   600      2
3    user3   650      3

__

SELECT id1, id2, name1, name2, img1, img2, total1, total2
FROM (
  SELECT
    C1.id AS id1, C1.img AS img1, C1.name AS name1,
    C2.id AS id2, C2.img AS img2, C2.name AS name2,
    C1.total AS total1, C2.total AS total2
  FROM users C1, users C2
  WHERE C1.id <> C2.id
    AND ABS(C1.total - C2.total) < 200
) as t
ORDER BY RAND()
LIMIT 1

结果

id1  | id2|  name1 | name2 | img1 | img2 |  total1 | total2
------------------------ -------------------------------------
1       3    user1   user3    1      3        500      650

有什么方法可以改善它吗?

3 个答案:

答案 0 :(得分:1)

确保为where子句中的所有列创建了索引:

CREATE TABLE `users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 255 ) NOT NULL ,
`total` INT NOT NULL ,
`img` INT NOT NULL ,
INDEX ( `total`)
);

另请注意,以下查询(不带子查询)将为您提供相同的结果

SELECT
  C1.id AS id1, C1.img AS img1, C1.name AS name1,
  C2.id AS id2, C2.img AS img2, C2.name AS name2,
  C1.total AS total1, C2.total AS total2
FROM users C1, users C2
WHERE C1.id <> C2.id 
  AND ABS(C1.total - C2.total) < 200
ORDER BY RAND()
LIMIT 1

您可以在sql fiddle

查看

答案 1 :(得分:1)

您可以优化内部SELECT,但使用ORDER BY RAND()总是会将性能提升到......我想不出比sh * t更好的词。基本上你是在告诉你的DBMS将所有索引都插入并重新排序整个数据集,一旦这个数据集变得超过“微小”,它将是一个非常明显的性能损失。

我今天刚刚写了another answer关于这一点,只要你不介意每次结果不一定不同,这应该有效。

修改

我刚刚注意到您只选择一个行。试试这个:

$rs = $dbh->query(
"SELECT COUNT(*) AS 'count'
 FROM users C1, users C2
 WHERE C1.id <> C2.id
   AND ABS(C1.total - C2.total) < 200");
$target = rand(0,$rs[0]['count']);
$rs = $dbh->query(
  "SELECT 
   C1.id AS id1, C1.img AS img1, C1.name AS name1,
   C2.id AS id2, C2.img AS img2, C2.name AS name2,
   C1.total AS total1, C2.total AS total2
  FROM users C1, users C2
  WHERE C1.id <> C2.id
   AND ABS(C1.total - C2.total) < 200
  LIMIT ?,1",
array($target));

它将使用您的索引,并且不需要重新排序可能较大的数据集。

以主键不等于自身的方式自行连接表也不是一个超级想法,你基本上是平方数据集的大小。具有1000行的表将产生具有999,000行的集合。我认为将“总”条件转换为明确的JOIN会降低性能,但我不确定。

变化:

  FROM users C1, users C2
  WHERE C1.id <> C2.id
   AND ABS(C1.total - C2.total) < 200

要:

  FROM users C1 INNER JOIN users C2
   ON C1.id <> C2.id
     AND ABS(C1.total - C2.total) < 200

答案 2 :(得分:0)

正如已经建议的那样,您可以使用索引对其进行优化。但是,我建议以编程方式执行“随机”逻辑。虽然对于较小的表使用ORDER BY RAND()非常好,但对于较大的表来说效率非常低(例如,如果你有一个包含10,000条记录的表,它必须生成10,000个随机数,而(AFAIK)选择最小的一个)。

我建议使用两个查询。选择COUNT,从中生成一个随机数,然后在LIMIT子句中使用该值。

示例:

//get the total number of rows
$result= mysql_query(" SELECT  COUNT(*) AS total FROM `table` ");
$row = mysql_fetch_array($result);
$total=$row['total'];

//create random value from 1 to the total of rows 
$randomvalue =rand(1,$total);

//get the random row
$result= mysql_query(" SELECT  * FROM `table` limit $randomvalue,1");


在您的特定情况下,您可以生成两个随机数并选择两个用户(只需确保随机数不相等)。

编辑:类似的例子找到here

相关问题