使用连接时为每个不同的字段值选择随机行

Posted

技术标签:

【中文标题】使用连接时为每个不同的字段值选择随机行【英文标题】:Select random row per distinct field value while using joins 【发布时间】:2021-03-30 03:48:22 【问题描述】:

我有一个显示一些帖子的 Wordpress 实例。每个帖子都以特定语言定义,并具有属性_post_year 集。所以我们可以有几篇文章使用相同的语言并引用同一年份。

mysql 表:

wp-posts

包含所有帖子。

ID | post_author | post_date | ...
==================================
 1 |         ...
 2 |         ...
...

wp_term_relationships

包含有关帖子语言的信息(除其他外)。

object_id | term_taxonomy_id | term_order |
===========================================
        1 |              ...
        1 |              ...
        2 |              ...
...

wp_postmeta

包含帖子元信息(如附加属性“_post_year”)。

meta_id | post_id | meta_key | meta_value |
===========================================
      1 |       1 |      ...
      2 |       1 |      ...
...

我曾经能够像这样加载每年一篇随机帖子(适用于所有可用年份):

SELECT DISTINCT
    wp_posts.*,
    postmeta.meta_value as post_meta_year
FROM (
    SELECT * FROM wp_posts
    JOIN wp_term_relationships as term_relationships
    ON term_relationships.object_id = wp_posts.ID
    AND term_relationships.term_taxonomy_id IN (LANGUAGE_ID)
    ORDER BY RAND()
) as wp_posts

JOIN wp_postmeta as postmeta
ON postmeta.post_id = wp_posts.ID
AND postmeta.meta_key = '_post_year'
AND post_status = 'publish'

GROUP BY post_meta_year DESC
ORDER BY post_meta_year DESC

自从我将 MySQL 升级到 5.7 版后,这不再起作用了:

Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'wp_posts.ID' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

我怎样才能使每年的随机帖子按降序排列?

【问题讨论】:

只是为了观察,10 年后,我还不需要在同一个查询中提供 DISTINCT 和 GROUP BY 为什么要升级到 3 年以上的 MySQL 版本? 好问题@GordonLinoff :) 升级是在一年前完成的(除了 5.7 之外,还有其他更多的选项,老实说,我从来没有想过,但会问他们),但注意到了具体​​问题就在上周。 【参考方案1】:

您可以尝试一种方法:从具有不同年份的派生表中选择年份,并在相关子查询中使用 ORDER BY rand()LIMIT 1 选择具有该年份的随机帖子 ID。将第二个派生表的结果与帖子连接起来。

SELECT po1.*,
       ppmo1.meta_value
       FROM (SELECT pmo1.meta_value,
                    (SELECT pi1.id
                            FROM wp_posts pi1
                                 INNER JOIN wp_postmeta pmi2
                                            ON pmi2.post_id = pi1.id
                                 INNER JOIN wp_term_relationships tri1
                                            ON tri1.object_id = pi1.id
                            WHERE tri1.term_taxonomy_id = LANGUAGE_ID
                                  AND pmi2.meta_key = '_post_year'
                                  AND pmi2.meta_value = pmo1.meta_value
                            ORDER BY rand()
                            LIMIT 1) id
                    FROM (SELECT DISTINCT
                                 pmi1.meta_value
                                 FROM wp_postmeta pmi1
                                 WHERE pmi1.meta_key = '_post_year') pmo1) ppmo1
            INNER JOIN wp_posts po1
                       ON po1.id = ppmo1.id
       ORDER BY ppmo1.meta_value DESC;

(未经测试,因为架构和示例数据不是由可消耗的 DDL 和 DML 提供的。)

【讨论】:

这非常有效。我在sqlfiddle.com/#!9/0e4c7d/4 创建了一个 sqlfiddle。谢谢@sticky-bit! 现在我试着绕过这个:)【参考方案2】:

在 MySQL 5.7 中,ONLY_FULL_GROUP_BY 模式默认启用(很高兴),我建议使用相关子查询进行过滤:

select *  -- better enumerate the actual column names here
from wp_posts p
inner join wp_postmeta pm on pm.post_id = p.id
where pm.meta_key = '_post_year' and p.id = (
    select pm1.post_id
    from wp_post p1
    inner join wp_postmeta pm1 on pm1.post_id = p1.id
    where p1.status = 'publish' and pm1.meta_key = '_post_year' and pm1.meta_value = pm.meta_value
    order by rand() limit 1
)

基本上,子查询为每组具有相同'_post_year' 的记录选择一个随机的post id,用于过滤查询。

请注意,使用此技术,无需在外部查询中再次过滤帖子状态,因为子查询已经完成并返回主键列。

【讨论】:

嗨@GMB,谢谢你的回答。我在sqlfiddle.com/#!9/0e4c7d/1/0 创建了一个 sqlfiddle,我刚刚创建了所有表(抱歉之前没有这样做)并测试了您的查询。不幸的是,结果以一种无用的方式变化。有时此查询不止一次返回一年,有时一年根本不存在。

以上是关于使用连接时为每个不同的字段值选择随机行的主要内容,如果未能解决你的问题,请参考以下文章

使用 sql (Hive) 中的条件为每个 ID 选择随机行

从Hive表中选择每列的随机行数

在 spark scala 中为数据帧中的每个组采样不同数量的随机行

选择 n 个随机行,其中 n 与每个值占总人口的百分比成比例

html5画布精灵表随机行/列

为spark scala中的数据框中的每个组采样不同数量的随机行