我有一个如下的SQL表
id |date_accessed
----------+------------
1 | 16/10/2014
1 | 28/10/2014
1 | 25/11/2014
1 | 16/12/2014
2 | 30/09/2014
2 | 03/10/2014
2 | 17/10/2014
2 | 03/01/2015
我需要按月和年对数据进行分组,但是我还想知道自用户第一次访问系统以来有多少个月
id | month | year | length_in_month
----------+------------+------------+-------------------
1 | 10 | 2014 | 1
1 | 11 | 2014 | 2
1 | 12 | 2014 | 3
2 | 09 | 2014 | 1
2 | 10 | 2014 | 2
2 | 01 | 2015 | 5
我的查询如下
select
id,
Extract(MONTH from "date_accessed") as month,
Extract(year from "date_accessed") as year
from
exampleTable
group by
1, 2, 3
order by
1, 3, 2
但是当我分组时,我无权访问min(date_accessed)
来获取length_in_month
列的长度。
有解决方案吗?
答案 0 :(得分:2)
我已经使用AGE
函数来确定首次访问的月份的开始日期与实际访问的日期的结束日期之间的差,以得出一个间隔正如您提到的,可以算是一个月,然后再加上1。这样可以得到预期的结果。
first_access
是在CTE中单独计算的,因为它是每个ID的单个值,而不是每个ID,月,年的单个值。
with m AS
(
select id, min(date_accessed)
as first_access from t
group by id
)
select t.id, Extract(MONTH from "date_accessed") as month,
Extract(year from "date_accessed") as year,
EXTRACT ( month from
MIN( AGE( date_trunc('month', date_accessed)
+ interval '1 month - 1 day', --last day of month
date_trunc('month', first_access) --first day of month
))
) + 1 as length_in_month
from t join m on t.id = m.id
group by t.id,month,year
order by 1,3,2;
答案 1 :(得分:1)
使用子查询,如下所示:
SELECT
exampleTable.id,
EXTRACT(month FROM "date_accessed") AS month,
EXTRACT(year FROM "date_accessed") AS year,
/* Calculate # months since the user accessed the system for the 1st time */
(EXTRACT(year from "date_accessed") - EXTRACT(year from firstTimeAccessDatesTable.firstAccessDate)) * 12
+ (EXTRACT(month from "date_accessed") - EXTRACT(month from firstTimeAccessDatesTable.firstAccessDate)) + 1 AS length_in_month
FROM
/* Join exampleTable with firstTimeAccessDatesTable by id */
exampleTable
INNER JOIN(
/* Perform subquery to obtain the date a given user accessed the system for the first time */
SELECT
id,
MIN("date_accessed") AS firstAccessDate
FROM
exampleTable
GROUP BY
1
) AS firstTimeAccessDatesTable
ON exampleTable.id = firstTimeAccessDatesTable.id
GROUP BY
1, 2, 3, 4
ORDER BY
1, 3, 2
答案 2 :(得分:0)
我认为您首先需要按ID选择Id和min(month)组,因此您将获得每个ID的第一个日期。 然后是另一个选择,例如您所做的选择,再加上我上面建议的选择。
答案 3 :(得分:0)
以下查询提供了确切的持续时间(以月为单位)。记住上面的示例输入,如果时差小于30天,则查询将给您length_in_months持续时间为0。乘以-1可以将负持续时间转换为正值。
create table Test(id integer, date_accessed date);
insert into Test values(1, "2014-10-16");
insert into Test values(1, "2014-10-28");
insert into Test values(1, "2014-11-25");
insert into Test values(1, "2014-12-16");
insert into Test values(2, "2014-09-30");
insert into Test values(2, "2014-10-03");
insert into Test values(2, "2014-10-17");
insert into Test values(2, "2015-10-16");
select a.id, a.month, a.year, a.date_accessed, (timestampdiff(MONTH,
a.date_accessed, a.min_date)) * -1 as length_in_month from (
select id, EXTRACT(MONTH FROM date_accessed) as MONTH, EXTRACT(YEAR FROM
date_accessed) as YEAR, date_accessed, (select MIN(date_accessed) from Test) as
min_date from Test order by date_accessed) a order by a.id asc;
Output
1 10 2014 2014-10-16 0
1 10 2014 2014-10-28 0
1 11 2014 2014-11-25 1
1 12 2014 2014-12-16 2
2 9 2014 2014-09-30 0
2 10 2014 2014-10-03 0
2 10 2014 2014-10-17 0
2 10 2015 2015-10-16 12
答案 4 :(得分:0)
使用以下内容
实时测试:http://sqlfiddle.com/#!17/7c833/6
-- drop table t;
/*
create table t as
select id, date_accessed::date
from (values
(1, '2014-10-16'),
(1, '2014-10-28'),
(1, '2014-11-25'),
(1, '2014-12-16'),
(2, '2014-09-30'),
(2, '2014-10-03'),
(2, '2014-10-17'),
(2, '2015-01-03')
) as x(id, date_accessed)
*/
with unique_months as
(
select
id,
date_trunc('month', date_accessed) as monthify
from t
group by id, monthify
)
, compute_length as
(
select
id, monthify,
(
(
extract(year from monthify) - extract(year from min(monthify) over(partition by id))
) * 12
)
+
(
extract(month from monthify) - extract(month from min(monthify) over(partition by id))
)
+
1 as length_in_month
from unique_months
)
select id,
extract(year from monthify) "year",
extract(month from monthify) "month",
length_in_month
from compute_length
order by id, monthify
结果:
| id | year | month | length_in_month |
|----|------|-------|-----------------|
| 1 | 2014 | 10 | 1 |
| 1 | 2014 | 11 | 2 |
| 1 | 2014 | 12 | 3 |
| 2 | 2014 | 9 | 1 |
| 2 | 2014 | 10 | 2 |
| 2 | 2015 | 1 | 5 |
答案 5 :(得分:0)
另一种方法
实时测试:http://sqlfiddle.com/#!17/7c833/2
-- drop table t;
/*
create table t as
select id, date_accessed::date
from (values
(1, '2014-10-16'),
(1, '2014-10-28'),
(1, '2014-11-25'),
(1, '2014-12-16'),
(2, '2014-09-30'),
(2, '2014-10-03'),
(2, '2014-10-17'),
(2, '2015-01-03')
) as x(id, date_accessed)
*/
with unique_months as
(
select
id,
extract(year from date_accessed) "year",
extract(month from date_accessed) "month",
min(date_accessed) as month_representative
from t
group by id, year, month
)
, compute_length as
(
select
id, year, month,
(
(
extract(year from month_representative) - extract(year from min(month_representative) over(partition by id))
) * 12
)
+
(
extract(month from month_representative) - extract(month from min(month_representative) over(partition by id))
)
+
1 as length_in_month
from unique_months
)
select *
from compute_length
order by id, year, month
结果:
| id | year | month | length_in_month |
|----|------|-------|-----------------|
| 1 | 2014 | 10 | 1 |
| 1 | 2014 | 11 | 2 |
| 1 | 2014 | 12 | 3 |
| 2 | 2014 | 9 | 1 |
| 2 | 2014 | 10 | 2 |
| 2 | 2015 | 1 | 5 |
答案 6 :(得分:0)
最短的查询,如果Postgres具有内置的DATEDIFF。
使用DISTINCT ON可以使查询更加惯用。
DISTINCT ON特定于Postgres。它丢弃重复的行并仅保留其中的一行,并根据传递给它的参数对行进行排序。
-- http://www.sqlines.com/postgresql/how-to/datediff
create or replace function month_diff (start_month date, end_month date)
returns int as $$
begin
return (date_part('year', end_month) - date_part('year', start_month))*12 +
date_part('month', end_month) - date_part('month', start_month);
end;
$$ language 'plpgsql' immutable;
select
distinct on (id, date_trunc('month', date_accessed))
id,
date_part('year', date_accessed) as year,
date_part('month', date_accessed) as month,
month_diff( min(date_accessed) over(partition by id), date_accessed ) + 1
as length_in_month
from t;
输出: