如何在SQL中获取最接近请求参数的值?

时间:2019-05-27 10:11:47

标签: sql postgresql

我有一张表格(shop_staff_relationvm)保存了员工和商店之间的关系。

它有3列:id_shopid_staffchange_date

  • id_staff =>人员外键
  • id_shop =>购买外键
  • change_date ==>每当创建新的职员商店关系时,我都会保存设置日期(职员商店可能会多次更改)

3个abov列代表shop_staff_relationvm表的可嵌入主键。

在查询中,我有2个请求参数:idShop和一个日期。

我正在尝试获取特定月份(idShop)中属于特定商店(requestMonth)的员工列表。

因此,当requestMonth位于员工与商店之间的关系的第一个日期与关系的最后日期之间时,我需要获取员工列表。

这是一个示例:

STAFF     | SHOP     | CHANGE DATE
----------+----------+-------------
Staff A   | Shop 1   | 10-01-2019
Staff A   | Shop 2   | 10-03-2019
Staff A   | Shop 3   | 10-05-2019

假设我们有requestMonth = 02-2019

然后:商店1将拥有员工A,商店2将没有员工,商店3将没有员工

假设我们有requestMonth = 03-2019

然后:商店1将没有员工,商店2将拥有员工A,商店3将没有员工

假设我们有requestMonth = 06-2019

然后:商店1没有员工,商店2没有员工,商店3没有员工A

这是我想出的公式:

first date of relation <= requestMonth <= first date of next relation

获取关系的第一个日期没有问题,但是获取下一个关系的第一个日期(我得出结论是离关系的第一个最近的值)时遇到了问题。

PS:我正在使用JPA本机查询

我尝试使用order by (change_date - first date of relation)来获取与第一个关系日期最接近的值。却总能为我带来最大值(在上述示例中,总能给我05-2019月)

我还尝试使用LEAD来获取下一个最接近的值。但是,使用上面的示例:

  

对于requestMonth = 02-2019:   LEAD等于03-2019 =>正确

     

对于requestMonth = 03-2019:   LEAD等于01-2019 =>错误=>应该为05-2019

这是整个查询:

SELECT s.* FROM staffvm s INNER JOIN shop_staff_relationvm ss ON ss.id_staff = s.id_staff WHERE ss.id_shop = 2 and 
(CASE WHEN ((SELECT MAX(to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM')) FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff) > to_date('2019-02-16', 'yyyy-MM') 
and (SELECT MIN(to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM')) FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff) <= to_date('2019-02-16', 'yyyy-MM') ) 
THEN 
((SELECT ((LEAD (to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM')) OVER (ORDER BY ss.id_staff DESC)) > to_date('2019-02-16', 'yyyy-MM') ) 
FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff and ss.id_shop !=2 LIMIT 1)
and 
(SELECT (MIN(to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM')) <= to_date('2019-02-16', 'yyyy-MM'))
FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff and ss.id_shop = 2) ) 
ELSE 
(SELECT (to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM') <= to_date('2019-02-16', 'yyyy-MM')) 
 FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff and ss.id_shop = 2 ) 
END);

我有问题的查询部分:

((SELECT ((LEAD (to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM')) OVER (ORDER BY ss.id_staff DESC)) > to_date('2019-02-16', 'yyyy-MM') ) 
FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff and ss.id_shop !=2 LIMIT 1)
and 
(SELECT (MIN(to_date(to_char(ss.change_date, 'yyyy-MM'), 'yyyy-MM')) <= to_date('2019-02-16', 'yyyy-MM'))
FROM shop_staff_relationvm ss WHERE ss.id_staff = s.id_staff and ss.id_shop = 2))

1 个答案:

答案 0 :(得分:1)

如果您想在特定日期安排工作人员,可以使用distinct on

select distinct on (ss.id_staff) ss.*
from shop_staff_relationvm ss 
where ss.change_date <= '2019-02-01'::date
order by ss.id_staff, ss.change_date desc;

shop_staff_relationvm(id_staff, change_date)上有一个索引,这可能是可能的解决方案中性能最好的。

要确定谁在特定商店,请使用子查询:

select s.*
from (select distinct on (ss.id_staff) ss.*
      from shop_staff_relationvm ss 
      where ss.change_date <= '2019-02-01'::date
      order by ss.id_staff, ss.change_date desc
     ) s
where s.shop_id = 2;