postgresql max(count(*)) - php

时间:2014-06-25 15:49:23

标签: php sql postgresql

我在postgresql中遇到了问题。 我有一个人(聚会的人),我想计算这个人的人数。

开始日期:" 2014-09-01",结束日期:" 2014-11-30"。

  • 我在09/01和09/22之间有5个人
  • 我在09/20和09/25之间有5个人
  • 我在09/26至10/05期间有5人
  • 我在10/01和11/30之间有5个人

我想在SQL(或PHP)中的开始日期和结束日期之间的每个月的最大住宿量。预计最高人数:

  • 九月(09)=> 10
  • 十月(10)=> 10
  • 十一月(11)=> 5

2 个答案:

答案 0 :(得分:1)

在给定时间内,每个月查找一天中同时出现的最多人数。

我建议您generate_series()生成您期间的一系列日子。然后聚合两次:

  • 首先得到每天的计数。一天可以处理普通BETWEEN。您的范围显然与包含边界有关。

  • 其次是每月获得最高金额。

SELECT date_trunc('month', day)::date AS month, max(ct) AS max_ct
FROM  (
   SELECT g.day, count(*) AS ct
   FROM   cohorte
         ,generate_series('2014-09-01'::date  -- first of Sept.
                         ,'2014-11-30'::date  -- last of Nov.
                         ,'1 day'::interval) g(day)
   WHERE  g.day BETWEEN t_begin AND t_end
   GROUP  BY 1
   ) sub
GROUP  BY 1
ORDER  BY 1;

返回:

month      |  max_ct
-----------+--------
2014-09-01 | 10
2014-10-01 | 10
2014-11-01 | 5

使用to_char()来美化月份输出。

SQL Fiddle ..已关闭ATM。这是我的测试用例(应该提供的):

CREATE TEMP TABLE cohorte (
   cohorte_id serial PRIMARY KEY
  ,person_id  int  NOT NULL
  ,t_begin    date NOT NULL  -- inclusive
  ,t_end      date NOT NULL  -- inclusive
);

INSERT INTO cohorte(person_id, t_begin, t_end)
SELECT g, '2014-09-01'::date, '2014-09-22'::date
FROM   generate_series (1,5) g
UNION ALL
SELECT g+5, '2014-09-20', '2014-09-25'
FROM   generate_series (1,5) g
UNION ALL
SELECT g+10, '2014-09-26', '2014-10-05'
FROM   generate_series (1,5) g
UNION ALL
SELECT g+15, '2014-10-01', '2014-11-30'
FROM   generate_series (1,5) g;

对于更复杂的检查,我建议OVERLAPS运算符:
Find overlapping date ranges in PostgreSQL

对于更复杂的情况,我还要考虑范围类型:
Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL

答案 1 :(得分:0)

你不能使用窗口功能吗? 我会尝试这样的事情(我没有测试过这段代码,只是暴露了我的想法)

SELECT max(count) FROM (
  SELECT count(*) OVER (PARTITION BY ???) as count
  FROM contract
  WHERE daterange(dateStart, dateEnd, '[]') && daterange('2014-09-01', '2014-10-01', '[)')
) as max

在这里,我的问题仍然是我找不到分区的每一天的分区方法。也许这是一种错误的方法,但我会对基于Windows的解决方案感兴趣。


编辑:使用此请求,您可以同时存在最大值,但不是一直存在,不仅仅是给定月份

with presence as (
  SELECT id, generate_series(begin_date, end_date, '1 day'::interval) AS date
  FROM test
),
presents as (
  SELECT count(*) OVER (PARTITION BY date) AS count
  FROM presence
)
SELECT max(count) from presents;

我们来了,我想

想象一下你的人员表有3列:

  • ID
  • entrance_date
  • leaving_date

请求看起来像

WITH presents as (
  SELECT id, 
         daterange(entrance_date, leaving_date, '[]') * daterange('2014-09-01', '2014-11-30', '[]') as range
  FROM person
  WHERE daterange(entrance_date, leaving_date, '[]') && daterange('2014-09-01', '2014-11-30', '[]')
),
present_per_day as (
  SELECT id,
         generate_series(lower(range), upper(range), '1 day'::interval) AS date
  FROM presents
),
count_per_day as (
  SELECT count(*) OVER (PARTITION BY date) AS count,
         date
  FROM present_per_day
),
SELECT max(count) OVER (PARTITION BY date_part('year', date), date_part('month', date)) as max,
       date_part('year', date),
       date_part('month', date)
FROM count_per_day;

(我必须离开,我希望我稍后有时间进行测试)

事实上,@ erwin解决方案比这个更容易和有效。