0

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

我生成长 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 部分都“使用临时;使用文件排序”。

表定义:

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;

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

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

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

  1. 我通过使UNION为

    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 字段

4

1 回答 1

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 并事先计算你的随机数:

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

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

至少这部分查询是闪电般的。

于 2013-09-25T10:54:27.237 回答