【问题标题】:Get row filtering by max(col) in postgresql在 postgresql 中通过 max(col) 获取行过滤
【发布时间】:2016-09-28 13:11:27
【问题描述】:

我正在使用 postgresql,但我不熟悉它。该代码有效,但我想知道是否可以以更直接的方式编写它。在这里,我加入 bar 以在子查询中加入 bar。我希望有一些简单的东西,比如select * from bar group by baz using max(z)

select *
from foo f
join bar b on(f.baz=b.baz AND b.z in (select max(z) from bar group by baz))
where uid1 = 120

【问题讨论】:

  • uid1barfoo 的列吗?最好的查询取决于它。 Postgres 版本和表定义也很重要。
  • @ErwinBrandstetter uid 来自 bar,但实际上我只是在学习如何以更简单的方式完成这个常用的行文。看来explain 更喜欢 distinct/orderby 方法,我也喜欢。
  • Postgres 查询计划器更喜欢什么(你可以通过EXPLAIN 看到),很大程度上取决于完整的画面。使用大表,您的查询可以以一种或另一种方式更快。与小桌子无关紧要。但是性能优化超出了这个问题的范围。

标签: sql postgresql greatest-n-per-group


【解决方案1】:

只需使用distinct on:

select distinct on (f.baz) *
from foo f join
     bar b
     on f.baz = b.baz 
where uid1 = 120
order by f.baz, b.z desc;

【讨论】:

    【解决方案2】:

    @Gordon's answer 几乎但不完全正确。如果列可以是NULLmax(b.z)ORDER BY b.z DESC 之间存在细微差别。经常被忽视并引起很多头痛。

    这可能会咬你一口,即使列 b.z 定义为 NOT NULL。外连接可以为列引入NULL 值。在这个简单的查询中无效,但可以在更复杂的查询中。

    虽然 minmaxcount 等聚合函数 忽略 NULL 值,但这些必须处理以某种方式在ORDER BY 子句中使用同一列时。
    在默认排序顺序 NULL 值排序 last。当使用DESC 颠倒排序顺序时,NULL 值排序first。必须这样才能保持一致。
    因此,如果有任何NULL 值在对等点集合中,那么您会从DISTINCT ON 获得带有baz IS NULL 的一行,您将从max(b.z) 获得最大的非空值(如果有)。很可能不是你想要的。您可以使用 NULLS LAST 修复它:

    另外,在使用SELECT * 时,您不希望两次返回连接列baz - 保证是相同的。 USING 子句派上用场了,它只返回列once

    所以:

    SELECT DISTINCT ON (baz) *
    FROM   foo f
    JOIN   bar b USING (baz)
    WHERE  uid1 = 120   -- where from?
    ORDER  BY baz, b.z DESC NULLS LAST;
    

    NULLS LAST 不会受到伤害,即使没有 NULL 值 - 除非您真的希望 NULL 值占上风。

    DISTINCT ON详解:

    更快

    由于uid1 来自baz (as commented),因此对于大表,此查询通常更快:

    SELECT *
    FROM   foo f
    JOIN  (
       SELECT DISTINCT ON (baz) *
       FROM   bar
       WHERE  uid1 = 120
       ORDER  BY baz, z DESC NULLS LAST
       ) b USING (baz);
    

    根据表定义和数据分布,可能会有更快的查询技术:

    【讨论】:

    • 我的实际代码是where uid1 = :uid OR uid2=:uid uid 是当前登录的用户或管理员冒充的任何人。该列从不/不为空
    • @acidzombie24:如果您对性能优化感兴趣,我建议您编写一个新问题,在其中提供准确的表定义(CREATE TABLE 语句)和 Postgres 版本以及实际查询。这里的简化可能会产生误导。您可以在此处添加评论以引起我的注意。
    【解决方案3】:


    您好,
    您可以进行以下查询,

    SELECT * FROM foo f
    INNER JOIN (SELECT baz, MAX(z) FROM bar GROUP BY baz) b
    ON (f.baz = b.baz)
    WHERE f.uid1 = 120;
    

    只需将 bar 与派生的 MAX(z) 连接起来并填充您所需的值。 uid1 必须来自 foo 表,因为 f 用作别名,如果不更改为 b.uid1

    【讨论】:

    • 我不清楚我的原始代码,因为我很懒,只是放了*,但我需要访问整行(bar 或 b.*)。这就是我加入 bar 的原因。
    猜你喜欢
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多