MySQL:UNION 和许多 ORDER BY RANDOM

标签 mysql sql-order-by union

我正在尝试为 MySQL 编写高效的 SQL 代码来获取一些值,但顺序是随机的,数量也不同。问题是表相当大(约 4m 行,约 400 MB),而且我没有太多时间来完成它(目前每次尝试大约需要约 1-2 分钟)。另外,每列都有索引,但不是唯一的,它是字符串值,而不是自动增量值。

我正在生成长 SQL 查询:

(SELECT fieldA,'id1' AS id FROM myTable WHERE (fieldB LIKE 'xxxx:%') ORDER BY RAND() LIMIT 7)
UNION ALL
(SELECT fieldA,'id2' AS id FROM myTable WHERE (fieldB ='123123') ORDER BY RAND() LIMIT 5)
etc...

我只想订购一次这张 table (这需要很多时间)。我已经尝试过了:

我对最后一个只有运气(III.来自OP的建议),但是“神奇”的数字16并没有起到作用 - 这对于较小的表很有用,而不是对于大约4000000行的表。

这是示例解释的输出:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   PRIMARY     myTable     range   fieldB      fieldB      143     NULL    64198   Using where; Using temporary; Using filesort
2   UNION   myTable     ALL     NULL    NULL    NULL    NULL    4386943     Using where; Using temporary; Using filesort
3   UNION   myTable     range   fieldB      fieldB      143     NULL    34374   Using where; Using temporary; Using filesort
4   UNION   myTable     ref     fieldB      fieldB      143     const   1999    Using where; Using temporary; Using filesort
5   UNION   myTable     range   fieldB      fieldB      143     NULL    1   Using where; Using temporary; Using filesort    NULL
UNION RESULT    <union1,2,3,4,5>    ALL     NULL    NULL    NULL    NULL    NULL    

所以我的猜测是 ORDER BY RAND 是主要问题 - 它使每个 UNION 部分都“使用临时;使用文件排序”。

表定义:

CREATE TABLE IF NOT EXISTS `myTable` (
  `fieldA` varchar(42) NOT NULL,
  `XYZ` varchar(36) NOT NULL,
  `fieldB` varchar(47) NOT NULL,
  KEY `fieldA` (`fieldA`),
  KEY `XYZ` (`XYZ`),
  KEY `fieldB` (`fieldB`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

它只存储简单、短的字符串,但数量很多。

有什么建议吗?是否有不同的方法?

@edit,现在我使用 MySQL 和 PHP 来实现它:

  1. 我通过为

    建立 UNION 来获取 fieldB 所需值的列表
    SELECT fieldB, "xxxx:%" AS orygLike FROM myTable WHERE fieldB LIKE "xxxx:%" GROUP BY fieldB
    

    对于每个 UNIONed 查询等 - 仅适用于 LIKE 模式下的查询,如果这是“=”,我已经知道哪个 fieldB 是有效的:)

  2. 然后我就可以制作 fieldBVal => orygLIKE 的映射数组(例如 "xxxx:yyyy"=>"xxxx:%")

  3. 我列出了可以使用的 fieldA 的所有 ID,通过 ID 使用 WHERE id IN (id1,id2,id3...) - 这样我就有了所有可能的 ID使用。在这里,我将数组组合在一起,并使用 array_rand 选择随机 ID。

  4. 简单:

    SELECT * FROM myTable WHERE id IN (RndID1, RndID2, RndID3 etc...)
    

速度非常快并且效果很好:)

感谢 fancyPants 指出 ID auto-inc 字段

最佳答案

您的查询正在扫描表的所有行。

从你的解释中看到这一行

2   UNION   myTable     ALL     NULL    NULL    NULL    NULL    4386943     Using where; Using temporary; Using filesort

这也是一个巨大的性能 killer 。使用表别名来准确查看它是哪个查询,并查看是否可以通过调整索引来执行某些操作。

也许您还可以重写查询以仅对表进行一次排序,然后复合索引可能比拥有这 3 个单独的索引更好。

尝试一下这个查询(但请注意,它不能保证您获得 7 行 fieldB 如 'xxx:%' 和 5 行 fieldB = '123123' 等上):

SELECT 
fieldA, 
CASE WHEN fieldB LIKE 'xxxx:%' THEN 'id1'
     WHEN fieldB ='123123' THEN 'id2'
END AS id 
FROM myTable 
WHERE 
(fieldB LIKE 'xxxx:%') 
OR fieldB ='123123'
ORDER BY RAND() 
LIMIT 12 /*7 + 5*/

编辑:

“LIKE '%'”当然是无用的,因为它会选择每一行。它的字面意思是“给我任何东西”。如果你想让它变得超快,这里有一个想法:

添加这样的列:

ALTER TABLE yourTableName ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY;

然后你会得到表中可用的最大 id 并预先计算你的随机数:

SET @my_max := (SELECT MAX(id) FROM yourTableName);
SET @r := RAND() * @my_max;
SELECT * FROM yourTable WHERE id >= @r LIMIT 1;

如果您需要更多,请再做一次。我做了 >=LIMIT 1 而不是简单的 id = @r 以防您有时删除一些行。

至少这部分查询速度快如闪电。

关于MySQL:UNION 和许多 ORDER BY RANDOM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18994215/

相关文章:

mysql - 配置 Root MySQL 访问 - MySQL Logrotate

java - 可以在android上使用jdbc驱动吗?

sql - 按规则创建自定义 SQL 顺序

python - 在 django 模型查询集中按自定义属性排序的好方法是什么?

MySQL COUNT UNION ALL 语句的结果

java - 使用 JComboBox 从数据库中检索数据清除 JTable

php - 如何使用 PDO 插入数据库,其中要插入的列发生变化?

mysql - 限时再下单

php - 多个 MYSQL 语句作为单个语句执行

mysql - 使用 union,然后从第二行开始按 order by 排列数据