【问题标题】:Quickly Select Random Rows With Where Condition快速选择具有 where 条件的随机行
【发布时间】:2021-07-03 07:07:17
【问题描述】:

是否可以从表中快速选择随机行,同时还使用 where 条件?

例子:

SELECT * FROM geo WHERE placeRef = 1 ORDER BY RAND() LIMIT 1

这可能需要 10 多秒。

我发现了这个,有时很快,有时很慢:

(SELECT *
FROM geo
INNER JOIN ( SELECT RAND() * ( SELECT MAX( nameRef ) FROM geo ) AS ID ) AS t ON geo.nameRef >= t.ID
WHERE geo.placeRef = 1
ORDER BY geo.nameRef
LIMIT 1)

This 提供快速结果,前提是没有额外的 where 条件。

这是创建表:

CREATE TABLE `geo` (
 `nameRef` int(8) DEFAULT NULL,
 `placeRef` mediumint(7) unsigned DEFAULT NULL,
 `category` enum('continent','country','region','subregion') COLLATE utf8_bin DEFAULT NULL,
 `parentRef` mediumint(7) DEFAULT NULL,
 `incidence` int(9) unsigned NOT NULL,
 `percent` decimal(11,9) unsigned DEFAULT NULL,
 `ratio` int(11) NOT NULL,
 `rank` mediumint(7) unsigned DEFAULT NULL,
 KEY `placeRef_rank` (`placeRef`,`rank`),
 KEY `nameRef_category` (`nameRef`,`category`),
 KEY `nameRef_parentRef` (`nameRef`,`parentRef`),
 KEY `nameRef_placeRef` (`nameRef`,`placeRef`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin

注意该表有大约 5.5 亿行。

期望查询:查询placeRef = x的表;然后快速返回一行。

问题:像SELECT * FROM geo WHERE placeRef = 1 这样的查询最多可以提供大约 1500 万条结果。所以选择单个随机行很慢。

【问题讨论】:

    标签: mysql random sql-order-by where-clause


    【解决方案1】:

    这种技术是可变的,因为它取决于匹配行在表中的位置。

    假设nameRef 是表的PRIMARY KEY,则快速修复可能是添加此索引:

    INDEX(placeRef, nameRef)
    

    我们以后再讨论这个

    (目前)有 3 个索引使这个子查询非常快(因为前导 nameRef):

    ( SELECT MAX( nameRef ) FROM geo )
    

    在那之后,我对(placeRef, nameRef) 的建议将适用于这些:

    WHERE geo.placeRef = 1
    geo.nameRef >= t.ID
    

    我认为生成的查询应该一直很快。

    【讨论】:

    • 添加在上面。 nameRef 不相关。我正在寻找一个随机的nameRef。表没有主键,因为不需要自己识别行。 placeRef,nameRef 是唯一索引。
    • @KohjahBreese - 每张桌子都需要一个PRIMARY KEY。如果 (placeRef, nameRef) 对是唯一的且不为空,则将其设为 PK。这可能会加快您的查询速度。注意我特意把placeRef放在了前面。
    • @KohjahBreese - 但是,由于 nameRef 值的分布可能不均匀,您的查询将产生不那么随机的结果。
    【解决方案2】:

    这是在 1/100 秒内得出结果:

    SELECT * FROM geo where placeRef = 1 AND nameRef >= CEIL( RAND() * ( SELECT MAX( nameRef ) FROM forenameGeo ) ) LIMIT 1
    

    如果您在要查询的两个列上都有索引,则此方法效果很好。但是,您可能需要创建一个随机排序的新表。在我的表格中,nameRefs 倾向于按国家/地区分组。这会导致从少数结果中选择随机结果,因为大多数结果都围绕相同的 Id 分组。我需要创建一个随机排序的新表ORDER BY RAND(),其中每一行都有一个唯一的 ID。现在我搜索这个小得多的汇总表:

    SELECT * FROM geoSummary where placeRef = 1 AND nameRef >= CEIL( RAND() * ( SELECT MAX( id ) FROM geoSummary ) ) LIMIT 1
    

    虽然我已经在服务器端代码中保存了最大 ID,但要切断一直运行的 SELECT MAX 查询,在那里生成随机数并运行:

    SELECT * FROM geoSummary where placeRef = 1 AND nameRef >= :random_number LIMIT 1
    

    这提供了真正随机的结果。

    【讨论】:

      猜你喜欢
      • 2013-11-18
      • 2020-07-11
      • 2013-12-16
      • 1970-01-01
      • 2011-07-14
      • 2016-04-27
      • 2016-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多