【问题标题】:Is this a perfect SQL query to fetch random items这是获取随机项目的完美 SQL 查询吗
【发布时间】:2020-09-26 08:32:30
【问题描述】:

我有以下查询来获取随机作者的书籍并按书籍最后修改日期排序。 因为我是 SQL 新手,所以有些东西告诉我这个查询可以改进和简化。我将感谢 SQL 专家的帮助,如果这已经简化,那么我将认为自己是 SQL 专家:)。

SELECT b.id, bc.author FROM book_authors bc
INNER JOIN books b ON bc.book = b.id 
WHERE bc.author = (SELECT author FROM book_authors ORDER BY random() limit 1) AND b.status = 'GOOD'
GROUP BY b.id, bc.author  
ORDER BY MAX(b.modified_date) DESC
LIMIT 10 OFFSET 0 -- for pagination purposes

表结构

book_authors
book       author
1            1
2            3
3            1

books        
id    status     modified_date
1      GOOD       01-01-2010
2       GOOD      02-01-2010
3       GOOD      03-01-2010

authors
id
1
2
3 


寻找类似的输出

authorId               bookId     
1 (a random author)       3
1 (same random author)    1

【问题讨论】:

  • 你的预期输出是什么?
  • 更新了问题,这样可以吗?
  • 您只想要拥有“GOOD”书籍的作者吗?你关心性能吗?
  • @GordonLinoff 是的,是的,我愿意

标签: sql postgresql join


【解决方案1】:

您的查询不是随机抽取作者样本。它从book_authors 中抽取随机样本,这是不同的。特别是,拥有更多书籍的作者更有可能被选中。考虑:

author_id    book_id
    1           1
    1           2
    1           3
    2           4

此表中的“随机”样本选择1 的频率是2 的三倍。这种有偏见的结果不是我对“随机作者的书”的解释。

因此,您应该从authors 表中进行抽样。尽管不需要聚合,但您的查询结构是合理的(见下文):

SELECT b.id, bc.author
FROM book_authors bc INNER JOIN
     books b
     ON bc.book = b.id 
WHERE bc.author = (SELECT a.id FROM authors id ORDER BY random() LIMIT 1) AND
      b.status = 'GOOD'
ORDER BY b.modified_date DESC;

现在,这是否真的能达到你想要的效果很有趣。正如所写,Postgres 有两种运行方式:

  • 为查询运行一次子查询。那就是随机抽取一位作者并在整个过程中使用它。
  • WHERE 中每次比较运行一次子查询。

子查询通常会在每次运行时返回不同的结果(从技术上讲,子查询是“不确定的”)。因此,对于每个比较,第二种方法会有不同的随机作者 - 这不是您想要的结果。在实践中,Postgres 优化器(我认为)忽略了子查询的不确定性,只执行一次。

要解决这个潜在的问题,最好将逻辑移到FROM 子句;那么它只被评估一次:

SELECT b.id, bc.author
FROM (SELECT a.id
      FROM authors id
      ORDER BY random()
      LIMIT 1
     ) a INNER JOIN
     book_authors bc
     ON bc.author = a.id INNER JOIN
     books b
     ON bc.book = b.id 
WHERE b.status = 'GOOD'
ORDER BY b.modified_date DESC;

注意:使用ORDER BY random() LIMIT 1 获取一个随机行有效。但是,对于除小表之外的任何东西,在性能方面都是相当昂贵的。我不会养成使用它的习惯。

【讨论】:

  • 感谢您花时间为一个您不认识的陌生人写解释。谢谢你。查询效果很好。关于random你有什么建议吗?
  • @Eric 。 . .我建议提出一个新问题(或做一些研究)。正如我所说,对于小桌子来说很好。如果你正在学习 SQL,表可能很小。
【解决方案2】:

你可以试试下面的 - 你不需要 group by clauseMAX(b.modified_date) 按子句排序

SELECT b.id, bc.author 
FROM book_authors bc INNER JOIN books b ON bc.book = b.id 
where  b.status = 'GOOD'
and bc.author = (SELECT author FROM book_authors ORDER BY random() limit 1) 
ORDER BY b.modified_date DESC
LIMIT 10 

【讨论】:

    【解决方案3】:

    无需过滤。我会在子查询中进行随机选择,然后将其与书籍连接。我也看不出聚合的意义。

    所以:

    select b.id, bc.author 
    from (select * from book_authors order by random() limit 1) bc  -- just one random author
    inner join books b on bc.book = b.id 
    where b.status = 'GOOD'
    order by b.modified_date desc
    limit 10 offset 0
    

    【讨论】:

    • 另一个很棒的简化查询
    猜你喜欢
    • 2012-11-22
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多