我在postgresql中遇到了问题。 我有一个人(聚会的人),我想计算这个人的人数。
开始日期:" 2014-09-01",结束日期:" 2014-11-30"。
我想在SQL(或PHP)中的开始日期和结束日期之间的每个月的最大住宿量。预计最高人数:
答案 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列:
请求看起来像
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解决方案比这个更容易和有效。