繁体   English   中英

SELECT FROM Subquery性能

[英]SELECT FROM Subquery performance

我正在为我的项目寻找更多性能(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

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

确保为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小提琴检查

您可以优化内部SELECT ,但使用ORDER 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

正如已经建议的那样,您可以使用索引对其进行优化。 但是,我建议以编程方式执行“随机”逻辑。 虽然对于较小的表使用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");


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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM