0

我正在为我的项目(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

有什么办法可以改善吗?

4

3 回答 3

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进行检查

于 2013-03-07T22:34:32.453 回答
1

您也许可以优化 inner SELECT,但 usingORDER BY RAND()将始终将性能转变为...我想不出比 sh*t 更好的词了。基本上,您是在告诉您的 DBMS 引入所有索引并重新排序整个数据集,一旦该数据集变得不仅仅是“小”,它将对性能造成非常显着的影响。

我今天刚刚写了另一个关于这个的答案,只要你不介意结果不一定每次都不同,它应该可以工作。

编辑

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

$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
于 2013-03-07T22:39:12.377 回答
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");


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

编辑:在这里找到类似的例子。

于 2013-03-07T22:51:46.933 回答