我正在尝试为 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 (这需要很多时间)。我已经尝试过了:
- 甚至是临时表(没有成功,复制完整表需要太长时间),
- MySQL UNION 2 queries containing ORDER BYs ,
- How to quickly re-sort a MySQL table by one of the columns? ,
- How to quickly SELECT 3 random records from a 30k MySQL table with a where filter by a single query?
我对最后一个只有运气(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 来实现它:
我通过为
建立 UNION 来获取 fieldB 所需值的列表SELECT fieldB, "xxxx:%" AS orygLike FROM myTable WHERE fieldB LIKE "xxxx:%" GROUP BY fieldB
对于每个 UNIONed 查询等 - 仅适用于 LIKE 模式下的查询,如果这是“=”,我已经知道哪个 fieldB 是有效的:)
然后我就可以制作 fieldBVal => orygLIKE 的映射数组(例如
"xxxx:yyyy"=>"xxxx:%"
)我列出了可以使用的 fieldA 的所有 ID,通过 ID 使用
WHERE id IN (id1,id2,id3...)
- 这样我就有了所有可能的 ID使用。在这里,我将数组组合在一起,并使用 array_rand 选择随机 ID。简单:
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/