mysql - 从表中选择随机词

标签 mysql sql

假设我有以下基本的 MySQL 数据:

CREATE TABLE my_words (my_word VARCHAR(255));

INSERT INTO my_words VALUES ('dog');
INSERT INTO my_words VALUES ('cat');
INSERT INTO my_words VALUES ('tree');
INSERT INTO my_words VALUES ('ball');
INSERT INTO my_words VALUES ('life');
INSERT INTO my_words VALUES ('complex');
INSERT INTO my_words VALUES ('digeridoo');
INSERT INTO my_words VALUES ('hamster');
INSERT INTO my_words VALUES ('it');
INSERT INTO my_words VALUES ('house');
INSERT INTO my_words VALUES ('love');
INSERT INTO my_words VALUES ('zealous');
INSERT INTO my_words VALUES ('nevis');
INSERT INTO my_words VALUES ('mountain');
INSERT INTO my_words VALUES ('call');
INSERT INTO my_words VALUES ('nail');
INSERT INTO my_words VALUES ('rat');
INSERT INTO my_words VALUES ('hat');

SELECT CONCAT(w1.my_word, w2.my_word) joined
FROM my_words w1, my_words w2
WHERE LENGTH(CONCAT(w1.my_word, w2.my_word)) = 8
ORDER BY RAND() LIMIT 5;

我可以在最后编写 SQL 语句以生成由 2 个单词组成的 5 个随机串联字符串的列表,其中字符串的总长度为 8 个字符。

这对于像我在样本中得到的简单数据表来说效果很好。

但是,我正在使用的“真实”表格包含大约 6,200 行。

如果我尝试相同类型的语句,生成 5 个字符串需要 10 秒。

我猜 SQL 非常低效,因为它每次都在表中搜索两次,而且这些表没有以任何方式连接。

我想知道是否有一种更简单的方法可以从表中提取由 2 个单词组成的单词字符串,其中连接字符串的长度为 8 个字符(尽管这可以改变 - 我只是使用 8 作为示例)。

谢谢


更新 1

解释计划:

EXPLAIN
SELECT CONCAT(w1.fld_un, w2.fld_un) joined
FROM j_un w1
JOIN j_un w2 ON w1.fld_len = 8 - w2.fld_len
WHERE w2.fld_len < 8
ORDER BY RAND()
LIMIT 5;

id  select_type table   type    possible_keys   key     key_len     ref rows    Extra
1   SIMPLE      w2      range   un_len          un_len  5   \N      2694        Using where; Using temporary; Using filesort
1   SIMPLE      w1      ref     un_len          un_len  5   func    527         Using where

更新 2

我不确定它是否相关,但“fld_un”表有大约 6,200 行。

“单词”保存在“fld_un”列中。

表的结构是:

Field       Type            Null    Key     Default     Extra
fld_id      int(11)         NO      PRI     NULL        auto_increment
fld_un      varchar(255)    YES             NULL     
fld_cat_id  int(11)         YES     MUL     NULL     
fld_len     int(2)          NO      MUL     NULL    

这些索引存在于表中:

Keyname     Type    Cardinality Field
PRIMARY     PRIMARY 6318        fld_id
cat         INDEX   15          fld_cat_id
bob         INDEX   11          fld_len 

表上已经有主索引重要吗?我认为技术上不需要。

声明:

SELECT CONCAT(word1, word2) joined
FROM (
    SELECT w1.fld_un word1, w2.fld_un word2
    FROM j_un2 w1
    JOIN j_un2 w2 ON w1.fld_len = 8 - w2.fld_len
    WHERE w2.fld_len < 8
    ORDER BY RAND()
    LIMIT 5) x;

查询耗时 23.6805 秒

解释计划:

id  select_type     table       type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY         <derived2>  ALL     NULL            NULL    NULL        NULL    5    
2   DERIVED         w2          range   bob             bob     4           NULL    4627    Using where; Using temporary; Using filesort
2   DERIVED         w1          ref     bob             bob     4           func    527     Using where

当我按照 Thorsten Kettner 的建议修改“bob”索引以包含 2 列时:

Keyname     Type    Cardinality Field
bob         INDEX   11          fld_len, fld_un

并重新测试:

SELECT CONCAT(word1, word2) joined
FROM (
    SELECT w1.fld_un word1, w2.fld_un word2
    FROM j_un2 w1
    JOIN j_un2 w2 ON w1.fld_len = 8 - w2.fld_len
    WHERE w2.fld_len < 8
    ORDER BY RAND()
    LIMIT 5) x;

查询用了 30.3394 秒返回 5 行。

解释计划:

id  select_type     table       type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY         <derived2>  ALL     NULL            NULL    NULL        NULL    5    
2   DERIVED         w2          range   bob             bob     4           NULL    4211    Using where; Using temporary; Using filesort
2   DERIVED         w1          ref     bob             bob     4           func    527     Using where

更新 3

在没有“order by rand()”的情况下运行,它在 0.0011 秒内运行!

最佳答案

您可以添加一列,例如word_length 包含单词的长度,并在word_length 列上添加索引。通常情况下,包含可以从另一列派生的数据是糟糕的设计,但在这种情况下,为了性能起见,您需要打破纯度。然后您的查询可以使用使用此列的 JOIN 条件:

SELECT CONCAT(w1.my_word, w2.my_word) joined
FROM my_words w1
JOIN my_words w2 ON w1.word_length = 8 - w2.word_length
WHERE w2.word_length < 8
ORDER BY RAND()
LIMIT 5

您可以使用INSERTUPDATE 触发器自动填充word_length 列。

过滤到 5 行后进行串联也可能有帮助:

SELECT CONCAT(word1, word2) joined
FROM (
    SELECT w1.my_word word1, w2.my_word word2
    FROM my_words w1
    JOIN my_words w2 ON w1.word_length = 8 - w2.word_length
    WHERE w2.word_length < 8
    ORDER BY RAND()
    LIMIT 5) x

关于mysql - 从表中选择随机词,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28594612/

相关文章:

mysql - 在子查询中传递列不起作用

javascript - 在 php 中使用 mysql 枚举和 javascript 显示和隐藏元素?

MySQL 嵌套 Select BETWEEN 多行

sql - 根据另一个表中的数据更新 SQLite 表

sql - 使用 SQL 执行内部自连接的更好方法是什么?

sql - 使用 SQL CTE 表包含路径和所有子项

mysql - MySQL 中何时使用单引号、双引号和反引号

PHP/MySQL 报告库

php - 拉维尔 : How to delete rows from multiple table with same id with only 1 query?

mysql - 如何使用 UNION ALL 语句修复此 SQL?