MySQL:UNION和许多ORDER BY RANDOM

时间:2013-09-25 00:19:06

标签: mysql sql-order-by union

我试图为MySQL创建有效的SQL代码来获取一些值,但是以随机顺序和不同的数量。问题是表格很大(约4米的行,约400 MB),我没有太多时间去做(现在每次尝试大约需要1-2分钟)。此外,每列都有索引,但不是UNIQUE,它是字符串值,而不是自动包含。

我生成长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...

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

我只有最后一个运气(III。来自OP的建议),但是16的“魔术”数字并没有诀窍 - 这对于较小的表而言并不适用于具有~4000000行的表。

这是示例EXPLAIN的输出:

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部件制作“使用临时;使用filesort”。

表格定义:

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;

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

任何建议,meaby有不同的方法吗?

@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 =&gt;的映射数组。 orygLIKE(例如"xxxx:yyyy"=>"xxxx:%"

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

  4. 简单:

    SELECT * FROM myTable WHERE id IN (RndID1, RndID2, RndID3 etc...)
    
  5. 速度非常快,效果很好:)。

    感谢fancyPants指向ID auto-inc字段

1 个答案:

答案 0 :(得分:2)

你在那里有查询正在扫描表格的所有行。

从解释

中查看此行
2   UNION   myTable     ALL     NULL    NULL    NULL    NULL    4386943     Using where; Using temporary; Using filesort

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

也许你也可以重写你的查询只对表进行一次排序,然后复合索引甚至比拥有这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并预先计算您的randoms:

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,以防你删除某些行。

至少这部分查询是快速的。