【问题标题】:How to use SELECT DISTINCT with RANDOM() function in PostgreSQL?如何在 PostgreSQL 中使用 SELECT DISTINCT 和 RANDOM() 函数?
【发布时间】:2012-07-09 05:11:16
【问题描述】:

我正在尝试运行 SQL 查询以获取四个随机项目。由于表product_filterproduct 中有不止一个touple 我必须在SELECT 中使用DISTINCT,所以我得到了这个错误:

对于 SELECT DISTINCT,ORDER BY 表达式必须出现在选择列表中

但是如果我将RANDOM() 放在我的SELECT 中,它将避免DISTINCT 结果。

有人知道如何将DISTINCTRANDOM() 函数一起使用吗?以下是我有问题的查询。

SELECT DISTINCT
    p.id, 
    p.title
FROM
    product_filter pf
    JOIN product p ON pf.cod_product = p.cod
    JOIN filters f ON pf.cod_filter = f.cod
WHERE
    p.visible = TRUE
LIMIT 4
ORDER BY RANDOM();

【问题讨论】:

  • 为什么一定要加入product_filter?您似乎没有在查询中的任何地方使用它。
  • @EricPetroelje,我已经减少了查询,它更复杂,但基本上我正在使用表 product_filter 进行其他连接。我对问题进行了编辑以更好地说明它。

标签: sql postgresql select


【解决方案1】:

使用子查询。不要忘记表别名tLIMITORDER BY 之后。

    SELECT *
    FROM (SELECT DISTINCT a, b, c
          FROM datatable WHERE a = 'hello'
         ) t
    ORDER BY random()
    LIMIT 10;

【讨论】:

    【解决方案2】:

    您可以简化查询以避免先验问题:

    SELECT p.cod, p.title
    FROM   product p
    WHERE  p.visible
    AND    EXISTS (
        SELECT 1
        FROM   product_filter pf
        JOIN   filters f ON f.cod = pf.cod_filter
        WHERE  pf.cod_product = p.cod
        )
    ORDER  BY random()
    LIMIT  4;
    

    要点:

    • 结果中只有表 product 中的列,仅检查其他表是否存在匹配行。对于这种情况,EXISTS semi-join 可能是最快和最简单的解决方案。使用它不会将基表product 中的行相乘,因此您无需再次使用DISTINCT 删除它们。

    • LIMIT 必须排在最后,在 ORDER BY 之后。

    • 我将 WHERE p.visible = 't' 简化为 p.visible,因为这应该是一个布尔列。

    【讨论】:

    • 有趣的解决方案!我不熟悉EXISTS,但似乎是一个优化的选项。请告诉我一件事,SELECT 1 像布尔结果一样工作?
    • @MarcioSimao: SELECT 1 在这里几乎无关紧要。你可以用SELECT * 甚至SELECT NULL 代替它。 More in this related question.
    • @MarcioSimao:马说什么! :)
    【解决方案3】:

    你要么做一个子查询

    SELECT * FROM (
        SELECT DISTINCT p.cod, p.title ... JOIN... WHERE
        ) ORDER BY RANDOM() LIMIT 4;
    

    或者您尝试对这些相同的字段进行 GROUPing:

    SELECT p.cod, p.title, MIN(RANDOM()) AS o FROM ... JOIN ...
        WHERE ... GROUP BY p.cod, p.title ORDER BY o LIMIT 4;
    

    这两个表达式中哪一个的计算速度更快取决于表结构和索引;通过对 cod 和 title 进行适当的索引,子查询版本将运行得更快(cod 和 title 将从索引基数信息中获取,并且 cod 是 JOIN 所需的唯一键,因此如果您按 title、cod 和可见索引(用于WHERE),很可能根本就不会访问物理表。

    我不太确定第二个表达式是否也会发生这种情况。

    【讨论】:

    • 使用 GROUP BY 的解决方案很简单,我喜欢。但我不明白你为什么使用MIN() 函数。
    • 哦,那是因为任何非 GROUP BY 列都必须是聚合函数。 RANDOM() 不是,但 MIN(RANDOM()) 是,当然随机分布的最小值仍然是随机的。
    • 真的很喜欢 GROUP BY 解决方案。使用 ActiveRecord 易于实现。
    【解决方案4】:
    SELECT DISTINCT U.* FROM
    (
    
        SELECT p.cod, p.title FROM product__filter pf
    
          JOIN product p on pf.cod_product = p.cod
          JOIN filters f on pf.cod_filter = f.cod
    
        WHERE p.visible = 't' 
    
        ORDER BY RANDOM()
    
    ) AS U
    
    LIMIT 4
    

    这首先执行 RANDOM,然后执行 LIMIT。

    【讨论】:

    • 我很想听听那些更了解 Postgres 的人的意见。但是,我认为这不会返回随机行。我猜想“distinct”会对表格进行排序,而limit会根据排序顺序返回前四行。
    • @GordonLinoff,我同意。我认为您的解决方案保留了随机行,而此解决方案可能会破坏随机行,因为 RANDOM() 函数是使用重复值调用的,并且它取决于 DISTINCT 如何对行进行排序。但无论如何,非常感谢您的回答@HolgerBrandt,它对我有很大帮助!
    【解决方案5】:

    我认为你需要一个子查询:

    select *
    from (select DISTINCT p.cod, p.title
          from product_filter pf  join
               product p
               on pf.cod_product = p.cod
          where p.visible = 't'
         ) t
    LIMIT 4
    order by RANDOM()
    

    先计算不同的值,然后做极限。

    请注意,这确实会影响性能,因为在选择您想要的内容之前,此查询会对所有内容进行区分。这是否重要取决于表的大小以及您使用查询的方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 1970-01-01
      • 2023-04-02
      相关资源
      最近更新 更多