我只想选择有多个职位但在任何时候只持有一个职位的员工的身份。如果结束日期为NULL,则表示它是他/她的当前位置。
对于下面的例子,我想得到1,3。
id | position | start_date | end_date
----------------------------------------------
0 | staff | 2005-01-01 | 2006-01-01
0 | secretary | 2006-01-02 |
0 | assistant | 2006-01-02 |
1 | staff | 2005-01-01 | 2006-01-01
1 | assistant | 2006-01-02 |
2 | receptionist | 2005-01-01 |
3 | driver | 2005-01-01 | 2007-01-01
3 | operator | 2007-01-02 |
3 | intern | 2002-01-01 | 2002-03-01
答案 0 :(得分:2)
这可以通过两种方式来解决。如果您真的只需要ID,那么执行两步查询就是一个选项。
首先获取具有多个位置的所有行:
char A[1000];
FILE * fpointer;
fpointer=fopen("text.txt","r");
i=0;
while(!feof(fpointer))
{
fscanf(fpointer,"%c",&A[i]);
i=i+1;
}
fclose(fpointer);
for (i=0;i<100;i++)
{
printf("%c",A[i]);
}
return 0;
要获得同时拥有多个职位的人,您可以使用:
select s1.id
from staff s1
where exists (select 1
from staff s2
where s1.id = s2.id
and s1.position <> s2.position)
由于select s1.id
from staff s1
where exists (select 1
from staff s3
where s1.id = s3.id
and s1.position <> s3.position
and (s1.start_date, coalesce(s1.end_date, 'infinity'::date)) overlaps (s3.start_date, coalesce(s3.end_date, 'infinity'::date))
)
无法进行比较,因此我们需要将null
中的null
值替换为大于所有其他日期的日期。这就是end_date
的作用。 coalesce(s3.start_date, 'infinity'::date)
运算符检查重叠的日期范围。在合并中使用类似overlaps
的内容也可以,但使用date '9999-12-31'
可以使其更加明确(至少在我看来)
将这些与infinity
运算符组合使用时,可以得到所需的结果:
EXCEPT
对于您的示例数据,上述查询将返回:
select s1.id
from staff s1
where exists (select 1
from staff s2
where s1.id = s2.id
and s1.position <> s2.position)
except
select s1.id
from staff s1
where exists (select 1
from staff s3
where s1.id = s3.id
and s1.position <> s3.position
and (s1.start_date, coalesce(s1.end_date, 'infinity'::date)) overlaps (s3.start_date, coalesce(s3.end_date, 'infinity'::date))
)
;
如果您需要所有列和所有位置(而不是仅 ID),您可以采用不同的方法。
首先获取所有没有重叠位置的行:
id
--
1
3
上面还将包含id = 0且不再有效位的行,因此我们需要删除所有具有多个位置的行:
select s1.*
from staff s1
where not exists (select 1
from staff s2
where s1.id = s3.id
and s1.position <> s3.position
and (s1.start_date, coalesce(s1.end_date, 'infinity'::date)) overlaps (s2.start_date, coalesce(s2.end_date, 'infinity'::date))
)
对于您的样本数据,上面将返回:
select *
from (
select s1.*,
count(*) over (partition by s1.id) as cnt
from staff s1
where not exists (select 1
from staff s2
where s1.id = s2.id
and s1.position <> s2.position
and (s1.start_date, coalesce(s1.end_date, 'infinity'::date)) overlaps (s2.start_date, coalesce(s2.end_date, 'infinity'::date))
)
) t
where cnt > 1;
我不确定这些是否是最有效的方法,但我现在无法想到别的东西。
答案 1 :(得分:2)
-- First select all id's that have held more than one position: 0, 1, 3
SELECT id
FROM personnel
GROUP BY id
HAVING count(id) > 1
EXCEPT
-- Now remove id's that had an overlap in positions: 0
SELECT DISTINCT sub1.id
FROM (
SELECT id, position, daterange(start_date, end_date, '[]') AS period
FROM personnel) sub1
JOIN (
SELECT id, position, daterange(start_date, end_date, '[]') AS period
FROM personnel) sub2
ON sub1.id = sub2.id AND sub1.period && sub2.period AND sub1.position <> sub2.position;
这会使用the daterange
type,当您有一个开始和结束日期时,它总是很方便使用,因为它允许检查与&&
运算符的重叠。