我有两张表格如下
emp_id | start_date | End Date
------------------------------------------
1 | May-10-2017 | May-30-2017
1 | Jun-05-2017 | null
2 | May-08-2017 | null
emp_id | start_date | End Date | Rate
-----------------------------------------------
1 | May-20-2017 | Jun-30-2017 | 75
1 | Jul-01-2017 | null | 80
这两个表共享emp_id(员工ID)外键并加入这两个我应该能够:
我可以使用下面的连接查询实现结果的第一部分
select distinct emp_id from work_contracts
left join hourly_pay hr USING(emp_id)
where hr.emp_id is null
我被困在第二部分,我可能需要一个相关的子查询来告诉在work_assignments start_date之前没有启动的每小时支付表记录?或者还有其他方式吗?
答案 0 :(得分:3)
在内部查询中进行日期比较,然后将其包装以将其过滤为满足延迟付款标准的日期。
select * from (
select distinct c.emp_id,
case when c.start_date < hr.start_date then 1 else 0 end as latePay
from work_contracts c
left join hourly_pay hr USING(emp_id)
) result
where latePay = 1
答案 1 :(得分:2)
这提示between
条件有一些曲折,但我在连接中使用之间的运气非常糟糕。它们似乎在后面和末尾执行某种形式的交叉连接,然后过滤掉实际的连接where子句样式。我知道这不是很技术性的,但我从来没有在一个结果很好的连接中做过不相等的条件。
所以,这看似违反直觉,但我认为爆炸所有日期可能性实际上可能是你最好的选择。不知道你的日期范围实际有多大,很难说。
此外,我认为这实际上会同时满足您问题中的两个条件 - 通过告诉您所有没有相应工资率的工作任务。
针对您的实际数据进行尝试,看看它是如何工作的(以及需要多长时间)。
with pay_dates as (
select
emp_id, rate,
generate_series (start_date, coalesce (end_date, current_date), interval '1 day') as pd
from hourly_pay
),
assignment_dates as (
select
emp_id, start_date,
generate_series (start_date, coalesce (end_date, current_date), interval '1 day') as wd
from work_assignments
)
select
emp_id, min (wd)::date as from_date,
max (wd)::date as thru_date
from
assignment_dates a
where
not exists (
select null
from pay_dates p
where p.emp_id = a.emp_id
and a.wd = p.pd
)
group by
emp_id, start_date
结果应该是没有费率的所有工作分配范围:
emp from thru
1 '2017-05-10' '2017-05-19'
2 '2017-05-08' '2017-11-14'
很酷的是它还会删除任何重叠,其中部分工作分配。
- 编辑3/20/2018 -
根据您的要求,这里是对逻辑内容的细分。
with pay_dates as(
select
emp_id, rate,
generate_series (start_date, coalesce (end_date, current_date), interval '1 day') as pd
from hourly_pay
)
这会获取hourly_pay数据,并将其分解为每个员工的记录,每天:
emp_id rate pay date
1 75 5/20/17
1 75 5/21/17
1 75 5/22/17
...
1 75 6/30/17
1 80 6/01/17
1 80 6/02/17
...
1 80 today
接下来,
[implied "with"]
assignment_dates as (
select
emp_id, start_date,
generate_series (start_date, coalesce (end_date, current_date), interval '1 day') as wd
from work_assignments
)
对工作分配表有效地做同样的事情,只保留每一行中的“开始日期列”。
然后主要问题是:
select
emp_id, min (wd)::date as from_date,
max (wd)::date as thru_date
from
assignment_dates a
where
not exists (
select null
from pay_dates p
where p.emp_id = a.emp_id
and a.wd = p.pd
)
group by
emp_id, start_date
从上面的两个查询中得出。重要的部分是反连接:
not exists (
select null
from pay_dates p
where p.emp_id = a.emp_id
and a.wd = p.pd
)
确定当天该员工没有相应记录的每个工作任务。
所以从本质上讲,查询从两个表中获取数据范围,得出每个可能的日期组合,然后进行反连接以查看它们不匹配的位置。
虽然看似违反直觉,但要记录一条记录并将其分成多个记录,需要考虑两件事:
日期是非常有限的生物 - 即使在10年的数据中也只构成4,000个左右的记录,这对数据库来说并不多,即使乘以员工数据库也是如此。你的时间框架远不如此。
我使用=以外的联接非常非常糟糕,例如between
或>
。在背景中它似乎是笛卡尔人然后过滤结果。相比之下,爆炸范围至少可以控制数据爆炸发生的程度。
对于笑话,我用上面的示例数据做了这个,并提出了这个,实际上看起来很准确:
1 '2017-05-10' '2017-05-19'
2 '2017-05-08' '2018-03-20'
如果有任何不清楚的地方,请告诉我。
答案 2 :(得分:2)
您可以使用查询
实现第二部分 select distinct wc.emp_id
from (select emp_id, min(start_date) start_date from work_contracts group by emp_id) wc
join (select emp_id, min(start_date) start_date from hourly_pay group by emp_id) hr
on wc.emp_id = hr.emp_id
where wc.start_date < hr.start_date
答案 3 :(得分:1)
您可以使用daterange
类型解决此问题(因为,您基本上想要的是hourly_pay
表中缺少的范围。)。
我在其中使用了以下operators:
+
范围联盟-
范围减法&&
测试范围交叉点@>
测试范围限制使用这些和简单的left join
,您可以编写查询以找出hourly_pay
表中缺少的范围。
select wa.emp_id, lower(dr) start_date, upper(dr) - 1 end_date
from work_assignments wa
left join hourly_pay hp on wa.emp_id = hp.emp_id
and daterange(wa.start_date, wa.end_date, '[]') && daterange(hp.start_date, hp.end_date, '[]')
cross join lateral (select case
when hp is null then daterange(wa.start_date, wa.end_date, '[]')
else daterange(wa.start_date, wa.end_date, '[]')
+ daterange(hp.start_date, hp.end_date, '[]')
- daterange(hp.start_date, hp.end_date, '[]')
end dr) dr
where not exists (select 1
from hourly_pay p
where p.emp_id = wa.emp_id
and daterange(p.start_date, p.end_date, '[]') @> dr)
-- emp_id | start_date | end_date
----------+------------+-------------
-- 1 | 2017-05-01 | 2017-05-19
-- 2 | 2017-05-08 | (null)
答案 4 :(得分:1)
也许我对措辞感到有些不满,但这还不够吗?这将返回任何emp_id,其中有一个记录,其中每小时开始日期是在工作分配开始日期之后
select distinct wc.emp_id from work_contracts wc
left join hourly_pay hr USING(emp_id)
where hr.start_date > wc.start_date
答案 5 :(得分:1)
select distinct p.emp_id <br>
from hourly_pay p <br>
join work_assignments w on p.emp_id = w.emp_id <br>
where p.start_date < w.start_date <br>
根据原始问题中的规定要求:查找hourly_pay
start_date
晚于work assignments
start_date
的记录。同样,根据此处的数据,查询应返回emp_id
1(因为work_assignments.start_date
具有5月10日至2017年,而最早的hourly_pay.start_date
则发布于2017年5月20日)
这对我来说意味着他们只需要员工ID号。
答案 6 :(得分:1)
我会使用not exists
/ exists
:
select wa.empid
from work_assignments wa
where not exists (select 1 from hourly_pay hp where wa.emp_id = hp.emp_id);
和第二个:
select wa.*
from work_assignments wa
where not exists (select 1
from hourly_pay hp
where wa.emp_id = hp.emp_id and ep.start_date <= wp.start_date
);
关于(2)的问题非常特别。但是,我希望您能够在整个作业期间按小时付费,而不仅仅是开始日期。如果是这种情况,那么OP应该问一个新的问题。
答案 7 :(得分:1)
第二个查询很简单,
尝试以下查询
select distinct h.emp_id
from work_assignments w inner join hourly_pay h
on
w.emp_id = h.emp_id
and h.start_date > w.start_date;
答案 8 :(得分:1)
查看您的数据,我可以做出以下假设:
1)对于end_date为null的员工,可以有最多一条记录这个条件适用于两个表。
2)同一员工的多个记录日期不重叠当员工有多个记录(如Emp 1)时,他/她的日期不会像[jan 1 - feb 1]和下一个记录为[jan 15-feb 20]或[jan 15-null](它们必须是非重叠时段)。
考虑到这些,下面的查询应该适合你。
SELECT hourly_pay.*
FROM work_assignments
INNER JOIN hourly_pay USING(emp_id)
WHERE hourly_pay.start_date > work_assignments.start_date
AND ( hourly_pay.start_date < work_assignments.end_date
OR (work_assignments.end_date is null
AND hourly_pay.end_date is null) );
说明:查询连接emp_id上的两个表,然后过滤
的记录1)在hourly_pay中有start_date&gt; work_assignments中的start_date
- 和 -
2)在hourly_pay中使用start_date&lt; work_assignments中的end_date(这是必需的,因此我们可以避免比较两个表中不相关的时间段记录
-OR-
两个表记录的结束日期均为空,使用假设1(如上所述) 对于end_date为null的员工,最多可以有一条记录。
根据您的数据,此查询应返回hourly_pay中EMP 1的两个记录,作为start_date,其中包含&gt; work_assignments中的start_date。
如果您只需要EMP ID列表,您只需选择该列SELECT DISTINCT hourly_pay.emp_id ...(rest of the query)
答案 9 :(得分:1)
http://sqlfiddle.com/#!17/f4595/1
我建议您使用not exists
,而不是使用左连接然后过滤空值记录,它会更快地工作。
SELECT w.emp_id, 'missing in the hourly_pay table' FROM work_assignments w
WHERE NOT exists (SELECT 1 FROM hourly_pay h WHERE h.emp_id = w.emp_id)
记录hourly_pay start_date晚于工作分配start_date;
SELECT w.emp_id FROM work_assignments w
WHERE
NOT exist (
SELECT 1 FROM hourly_pay hp
WHERE
hp.start_date < w.start_date AND w.emp_id = hp.emp_id )
第二个查询实际上包含第一个查询的结果,因此您可以合并它们,如下所示:
SELECT
w.emp_id,
(CASE WHEN ( EXISTS
(SELECT 1 FROM hourly_pay h
WHERE
h.emp_id = w.emp_id ) )
THEN
'hourly_pay start_date is later'
ELSE
'missing in the hourly_pay table'
END)
FROM
work_assignments w
WHERE
NOT EXISTS (
SELECT
1
FROM
hourly_pay hp
WHERE
hp.start_date < w.start_date
AND w.emp_id = hp.emp_id
)
答案 10 :(得分:1)
这将很好地完成工作。
SELECT DISTINCT emp_id
FROM work_assingment
JOIN hourly_pay hr USING(emp_id)
WHERE hr.start_date < work_assingment.start_date;