【问题标题】:How do I get min, median and max from my query in postgresql?如何从我的 postgresql 查询中获取最小值、中值和最大值?
【发布时间】:2012-08-22 06:47:19
【问题描述】:

我写了一个查询,其中一列是一个月。从中我必须得到最小月、最大月和中月。以下是我的查询。

select ext.employee,
       pl.fromdate,
       ext.FULL_INC as full_inc,
       prevExt.FULL_INC as prevInc,
       (extract(year from age (pl.fromdate))*12 +extract(month from age (pl.fromdate))) as month,
       case
         when prevExt.FULL_INC is not null then (ext.FULL_INC -coalesce(prevExt.FULL_INC,0))
         else 0
       end as difference,
       (case when prevExt.FULL_INC is not null then (ext.FULL_INC - prevExt.FULL_INC) / prevExt.FULL_INC*100 else 0 end) as percent
from pl_payroll pl
  inner join pl_extpayfile ext
          on pl.cid = ext.payrollid
         and ext.FULL_INC is not null
  left outer join pl_extpayfile prevExt
               on prevExt.employee = ext.employee
              and prevExt.cid = (select max (cid) from pl_extpayfile
                                 where employee = prevExt.employee
                                 and   payrollid = (
                                   select max(p.cid)
                                   from pl_extpayfile,
                                        pl_payroll p
                                   where p.cid = payrollid
                                   and   pl_extpayfile.employee = prevExt.employee
                                   and   p.fromdate < pl.fromdate
                                 )) 
              and coalesce(prevExt.FULL_INC, 0) > 0 
where ext.employee = 17 
and (exists (
    select employee
    from pl_extpayfile preext
    where preext.employee = ext.employee
    and   preext.FULL_INC <> ext.FULL_INC
    and   payrollid in (
      select cid
      from pl_payroll
      where cid = (
        select max(p.cid)
        from pl_extpayfile,
             pl_payroll p
        where p.cid = payrollid
        and   pl_extpayfile.employee = preext.employee
        and   p.fromdate < pl.fromdate
      )
    )
  )
  or not exists (
    select employee
    from pl_extpayfile fext,
         pl_payroll p
    where fext.employee = ext.employee
    and   p.cid = fext.payrollid
    and   p.fromdate < pl.fromdate
    and   fext.FULL_INC > 0
  )
)
order by employee,
         ext.payrollid desc

如果不可能,是否可以获得最大月份和最小月份?

【问题讨论】:

  • 您的查询几乎难以辨认。我已经把它放在一个代码块中,但仍然不可能真正遵循。可能值得您花时间编辑您的问题并对其进行格式化以提高可读性;现在有些人会看着它,去“嘎!”继续前进而不试图回答。不过,我不知道该问题的查询是否重要;您只需要minmax 聚合函数。对于中位数,您是否尝试过 wiki.postgresql.org/wiki/Aggregate_Median ?第一次搜索“postgresql 中位数”

标签: postgresql


【解决方案1】:

要计算 PostgreSQL 中的中位数,只需取 50% 的百分位数(无需添加额外的函数或任何东西):

SELECT PERCENTILE_CONT(0.5) WITHIN GROUP(ORDER BY x) FROM t;

【讨论】:

  • PERCENTILE_DISC() 在许多情况下可能是首选。
  • 就像一个魅力,但请注意这是 postgres 9.4+!
  • 不错。我担心它不会平均长度集中的值,但它似乎运作良好。 SELECT PERCENTILE_CONT(0.5) WITHIN GROUP(ORDER by val) FROM generate_series(1, 4) as t(val); 返回 2.5。但是,PERCENTILE_DISC 返回 2。
  • 非常有用,但不适用于窗口函数。
【解决方案2】:

您需要名为 minmax 的聚合函数。请参阅 PostgreSQL 文档和教程:

PostgreSQL 中没有内置的中位数,但是已经实现并为 wiki 贡献了一个:

http://wiki.postgresql.org/wiki/Aggregate_Median

加载后,它的使用方式与minmax 相同。用 PL/PgSQL 编写会慢一些,但如果速度很重要,您甚至可以适应 C 版本。

更新评论后:

听起来您想在单个结果旁边显示统计汇总。您无法使用普通聚合函数执行此操作,因为您无法引用结果列表中 GROUP BY 之外的列。

您需要从子查询中获取统计信息,或将聚合用作窗口函数。

给定虚拟数据:

CREATE TABLE dummystats ( depname text, empno integer, salary integer );
INSERT INTO dummystats(depname,empno,salary) VALUES
('develop',11,5200),
('develop',7,4200),
('personell',2,5555),
('mgmt',1,9999999);

...加上the median aggregate from the PG wiki:

你可以用一个普通的聚合来做到这一点:

regress=# SELECT min(salary), max(salary), median(salary) FROM dummystats;
 min  |   max   |         median          
------+---------+----------------------
 4200 | 9999999 | 5377.5000000000000000
(1 row)

但不是这个:

regress=# SELECT depname, empno, min(salary), max(salary), median(salary)
regress-# FROM dummystats;
ERROR:  column "dummystats.depname" must appear in the GROUP BY clause or be used in an aggregate function

因为在聚合模型中将平均值与单个值一起显示是没有意义的。您可以显示组:

regress=# SELECT depname, min(salary), max(salary), median(salary) 
regress-# FROM dummystats GROUP BY depname;
  depname  |   min   |   max   |          median          
-----------+---------+---------+-----------------------
 personell |    5555 |    5555 | 5555.0000000000000000
 develop   |    4200 |    5200 | 4700.0000000000000000
 mgmt      | 9999999 | 9999999 |  9999999.000000000000
(3 rows)

...但听起来您想要单独的值。为此,您必须使用 window,这是 PostgreSQL 8.4 中的新功能。

regress=# SELECT depname, empno, 
                 min(salary) OVER (), 
                 max(salary) OVER (), 
                 median(salary) OVER () 
          FROM dummystats;

  depname  | empno | min  |   max   |        median         
-----------+-------+------+---------+-----------------------
 develop   |    11 | 4200 | 9999999 | 5377.5000000000000000
 develop   |     7 | 4200 | 9999999 | 5377.5000000000000000
 personell |     2 | 4200 | 9999999 | 5377.5000000000000000
 mgmt      |     1 | 4200 | 9999999 | 5377.5000000000000000
(4 rows)

另见:

【讨论】:

  • 如果我使用 max 和 min 方法,它要求将列的其余部分放在 group by 子句中,然后它也不起作用
  • @DeepakKumar 你需要阅读 PostgreSQL 教程。它解释了聚合、GROUP BY 等。猜测您需要通过子查询获取最小值、最大值和中值,或者需要使用窗口函数来计算它们。见postgresql.org/docs/current/static/tutorial-window.html
  • @DeepakKumar 我怀疑你需要窗口函数。请参阅上面的更新答案。由于没有示例数据,我无法运行您的查询,但我提供了一个简单的示例。我使用 avg() 来获得平均值,因为没有内置的中位数,尽管您可以通过该 wiki 代码添加一个。如果您将OVER () 添加到您的聚合中而不添加任何GROUP BY 它可能会起作用。
  • 或者,如果您想按部门汇总:min(salary) OVER (PARTITION BY depname) AS dep_min_salary
【解决方案3】:

中位数的另一种选择:

SELECT x
FROM table
ORDER BY x
LIMIT 1 offset (select count(*) from x)/2

【讨论】:

    【解决方案4】:

    要查找中位数: 例如考虑我们有 6000 行存在于表中。首先我们需要从原始表中取一半行(因为我们知道中位数总是中间值)所以这里 6000 的一半是 3000(取 3001 以获得精确的两个中间值)。

    SELECT *
          FROM (SELECT column_name
                FROM Table_name
                ORDER BY column_name
                LIMIT 3001)As Table1
          ORDER BY column_name DESC ---->Look here we used DESC(Z-A)it will display the last 
                                    --   two values(using LIMIT 2) i.e (3000th row and 3001th row) from 6000 
                                    --   rows  
          LIMIT 2;
    

    【讨论】:

      猜你喜欢
      • 2022-11-25
      • 2019-09-01
      • 2020-03-29
      • 2017-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-13
      • 2020-10-16
      相关资源
      最近更新 更多