更有效地查找两个日期之间有保险的员工

2024-01-02

我需要检索员工列表,以及每个员工在给定年份中积极享受福利的月份列表。有一个包含工作数据的表和一个包含福利信息的表。还有一个交付日期表,列出了 2007 年至 2018 年的每个日期,并显示了每个日期的月份、月份和日历年。

我现在编写查询的方式是:查找日期表中 1) 提示年份的 01/01 和 12/31 之间的所有日期(或当前日期,以较早者为准),2) 期间员工在福利表上活跃的时间。对于每个日期,我还需要工作表中的 deptid 以及截至该日期的福利表中的福利计划。然后我做了一个不同的,只显示每个员工的月份和日历年。

这是可行的,但当我尝试为有很多人的部门这样做时,问题就来了。我相信它需要很长时间才能运行,因为它为每个员工检索最多 365 行,然后只显示其中的 12 行,因为它只提取不同的月份。我觉得有更好的方法可以做到这一点,我只是想不出它是什么。

以下是我正在使用的表格的一些简化示例:

日期表

THE_DATE   MONTHOFYEAR   CALENDAR_YEAR
01-OCT-15  10            2015
02-OCT-15  10            2015
03-OCT-15  10            2015
...

职位表

(A=活动;I=不活动)

EMPLID     EFFDT         DEPTID           HR_STATUS
00123      01-FEB-15     900              A
00123      30-JUN-15     900              I
00123      01-AUG-15     901              A

福利表

EMPLID     EFFDT         BENEFIT_PLAN     STATUS
00123      01-MAR-15     PPO              A
00123      31-JUL-15                      I
00123      01-SEP-15     HMO              A

期望的结果

EMPLID     CALENDAR_YEAR MONTHOFYEAR      DEPTID         BENEFIT_PLAN
00123      2015          3                900            PPO
00123      2015          4                900            PPO
00123      2015          5                900            PPO
00123      2015          6                900            PPO
00123      2015          7                900            PPO
00123      2015          9                901            HMO
00123      2015          10               901            HMO
00123      2015          11               901            HMO
^ (shows November row even though employee was only covered for part of this month)

获取上述结果的 SQL 示例

SELECT DISTINCT J.EMPLID, D.CALENDAR_YEAR, D.MONTHOFYEAR, J.DEPTID, B.BENEFIT_PLAN
FROM DATES D, 
     JOBS J 
     JOIN 
     BENEFITS B 
     ON J.EMPLID = B.EMPLID
WHERE D.THE_DATE <= SYSDATE
AND D.THE_DATE BETWEEN 
        TO_DATE(:YEAR_PROMPT || '01-01', 'YYYY-MM-DD') 
        AND 
        TO_DATE(:YEAR_PROMPT || '12-31', 'YYYY-MM-DD')
AND B.STATUS = 'A'
AND D.THE_DATE BETWEEN 
        B.EFFDT 
        AND 
        NVL(SELECT MIN(B_ED.EFFDT) 
            FROM BENEFITS B_ED
            WHERE B_ED.EMPLID = B.EMPLID
            AND B_ED.EFFDT > B.EFFDT
        , SYSDATE)
AND J.EFFDT = (SELECT MAX(J_ED.EFFDT)
               FROM JOBS J_ED
               WHERE J_ED.EMPLID = J.EMPLID
               AND J_ED.EFFDT <= D.THE_DATE)

我可以不说“检索每个日期并检查它是否符合条件”,而是可以以某种方式改变逻辑以获得相同的结果,而无需翻阅这么多行?


是的;通过使用LEAD()分析函数,您可以计算职位和福利表中的下一个 effdt,这使得在范围之间查询更加容易。

就像是:

with dates as (select trunc(sysdate, 'yyyy') - 1 + level the_date,
                      to_number(to_char(trunc(sysdate, 'yyyy') - 1 + level, 'mm')) monthofyear,
                      to_number(to_char(sysdate, 'yyyy')) calendar_year
               from   dual
               connect by level <= 365),
      jobs as (select 123 emplid, to_date('01/02/2015', 'dd/mm/yyyy') effdt, 900 deptid, 'A' hr_status from dual union all
               select 123 emplid, to_date('30/06/2015', 'dd/mm/yyyy') effdt, 900 deptid, 'I' hr_status from dual union all
               select 123 emplid, to_date('01/08/2015', 'dd/mm/yyyy') effdt, 901 deptid, 'A' hr_status from dual),
  benefits as (select 123 emplid, to_date('01/03/2015', 'dd/mm/yyyy') effdt, 'PPO' benefit_plan, 'A' status from dual union all
               select 123 emplid, to_date('31/07/2015', 'dd/mm/yyyy') effdt, null benefit_plan, 'I' status from dual union all
               select 123 emplid, to_date('01/09/2015', 'dd/mm/yyyy') effdt, 'HMO' benefit_plan, 'A' status from dual),
-- ********* end of mimicking your tables ********* --
         j as (select emplid,
                      effdt,
                      deptid,
                      hr_status,
                      lead(effdt, 1, sysdate) over (partition by emplid order by effdt) next_effdt
               from   jobs),
         b as (select emplid,
                      effdt,
                      benefit_plan,
                      status,
                      lead(effdt, 1, sysdate) over (partition by emplid order by effdt) next_effdt
               from   benefits)
select distinct j.emplid,
                d.calendar_year,
                d.monthofyear,
                j.deptid,
                b.benefit_plan
from   j
       inner join dates d on (d.the_date >= j.effdt and d.the_date < j.next_effdt)
       inner join b on (j.emplid = b.emplid)
where  d.the_date <= sysdate
and    d.the_date between to_date (:year_prompt || '01-01', 'YYYY-MM-DD')
                      and to_date (:year_prompt || '12-31', 'YYYY-MM-DD') -- if no index on d.the_date, maybe use trunc(the_date, 'yyyy') = :year_prompt
and    b.status = 'A'
and    d.the_date between b.effdt and b.next_effdt
order by 1, 4, 2, 3;

    EMPLID CALENDAR_YEAR MONTHOFYEAR     DEPTID BENEFIT_PLAN
---------- ------------- ----------- ---------- ------------
       123          2015           3        900 PPO         
       123          2015           4        900 PPO         
       123          2015           5        900 PPO         
       123          2015           6        900 PPO         
       123          2015           7        900 PPO         
       123          2015           9        901 HMO         
       123          2015          10        901 HMO         
       123          2015          11        901 HMO   

(显然,你可以排除dates, jobs and benefits来自上述查询的子查询,因为您已经拥有这些表。它们仅出现在查询中以模拟包含该数据的表,而无需实际创建表。)。


ETA:这是一个仅根据传入的年份计算 12 个月的版本,这将日期行减少到 12 行,而不是 365/366 行。

不幸的是,您仍然需要不同的,以考虑当您有多行从同一个月开始的情况。

例如,对于以下示例中的数据,如果删除了非重复值,您最终将在第 6 个月得到 3 行。然而,不同的操作的行数将远远少于以前。

with dates as (select add_months(to_date(:year_prompt || '-01-01', 'YYYY-MM-DD'), - 1 + level) the_date,
                      level monthofyear,
                      :year_prompt calendar_year -- assuming this is a number
               from   dual
               connect by level <= 12),
      jobs as (select 123 emplid, to_date('01/02/2015', 'dd/mm/yyyy') effdt, 900 deptid, 'A' hr_status from dual union all
               select 123 emplid, to_date('15/06/2015', 'dd/mm/yyyy') effdt, 900 deptid, 'I' hr_status from dual union all
               select 123 emplid, to_date('26/06/2015', 'dd/mm/yyyy') effdt, 900 deptid, 'A' hr_status from dual union all
               select 123 emplid, to_date('01/08/2015', 'dd/mm/yyyy') effdt, 901 deptid, 'A' hr_status from dual),
  benefits as (select 123 emplid, to_date('01/03/2015', 'dd/mm/yyyy') effdt, 'PPO' benefit_plan, 'A' status from dual union all
               select 123 emplid, to_date('31/07/2015', 'dd/mm/yyyy') effdt, null benefit_plan, 'I' status from dual union all
               select 123 emplid, to_date('01/09/2015', 'dd/mm/yyyy') effdt, 'HMO' benefit_plan, 'A' status from dual),
-- ********* end of mimicking your tables ********* --
         j as (select emplid,
                      trunc(effdt, 'mm') effdt,
                      deptid,
                      hr_status,
                      trunc(coalesce(lead(effdt) over (partition by emplid order by effdt) -1, sysdate), 'mm') end_effdt
                        -- subtracting 1 from the lead(effdt) since here since the original sql had d.the_date < j.next_effdt and we need
                        -- to take into account when the next_effdt is the first of the month; we want the previous month to be displayed
               from   jobs),
         b as (select emplid,
                      trunc(effdt, 'mm') effdt,
                      benefit_plan,
                      status,
                      trunc(lead(effdt, 1, sysdate) over (partition by emplid order by effdt), 'mm') end_effdt
               from   benefits)
select distinct j.emplid,
                d.calendar_year,
                d.monthofyear,
                j.deptid,
                b.benefit_plan
from   j
       inner join dates d on (d.the_date between j.effdt and j.end_effdt)
       inner join b on (j.emplid = b.emplid)
where  d.the_date <= sysdate
and    b.status = 'A'
and    d.the_date between b.effdt and b.end_effdt
order by 1, 4, 2, 3;

    EMPLID CALENDAR_YEAR MONTHOFYEAR     DEPTID BENEFIT_PLAN                    
---------- ------------- ----------- ---------- --------------------------------
       123 2015                    3        900 PPO                             
       123 2015                    4        900 PPO                             
       123 2015                    5        900 PPO                             
       123 2015                    6        900 PPO                             
       123 2015                    6        900 PPO                             
       123 2015                    7        900 PPO                             
       123 2015                    9        901 HMO                             
       123 2015                   10        901 HMO                             
       123 2015                   11        901 HMO    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

更有效地查找两个日期之间有保险的员工 的相关文章

  • 将 2 个不同表中的 2 个值相乘

    我正在尝试使用 SQL 将值 X 乘以值 Y 值 X 位于表 A 中 B 位于表 B 中 我找不到这个问题的答案 表交易 ID Transaction ID Total Amount 1 001 1200 2 002 1500 3 003
  • 无法从 CursorWindow 读取第 0 行,第 -1 列?

    我在使用数据库时遇到问题 当我运行 SQLView java 时 出现致命异常 java lang RuntimeException Unable to start activity ComponentInfo com jacob eind
  • 确定自上次访问 SQL Server 以来的行更改

    我们有一个多用户系统 用户将数据保存到中央 SQL Server 2005 数据库中 我们遇到了一个问题 即一个用户刷新数据库中的更改 而另一个用户保存新数据 我们当前收集更改的方式是每个表上都有一个时间戳列 该列在每行插入 更新时都会填充
  • Django:ImageField 需要文件路径还是实际的图像对象?

    Running Windows 7 Python 3 3 Django 1 6 我对如何将图像存储为 Django 数据库中表的一部分感到困惑 有一个领域叫做ImageField 这是Docs https docs djangoprojec
  • 如何证明2条sql语句是等价的

    我开始用连接和子语句重写一个复杂的 SQL 语句 并获得一个看起来更简单的语句 我通过在相同的数据集上运行并获得相同的结果集来测试它 一般来说 我如何 概念上 证明这两个陈述在任何给定数据集中都是相同的 我建议学习关系代数 正如 Mchl
  • 使用连接字段的 SQL JOIN

    我有两个表 Table1 包含一列 该列构成 Table2 中列的部分值 例如 表1 XName 123456 表2 ZName ABC 123456 我需要创建一个与这些匹配的 JOIN 但是使用 MS SQL 2008 我在完成这项工作
  • Oracle 时间戳数据类型

    不带参数的时间戳数据类型和带参数 0 的时间戳数据类型有什么不同 timestamp VS timestamp 0 括号中的数字指定要存储的小数秒的精度 所以 0 意味着不存储任何一小部分秒 而仅使用整秒 如果未指定 则默认值为小数点分隔符
  • 将 mysql 查询输出存储到 shell 变量中

    我需要一个变量来保存从数据库检索的结果 到目前为止 这基本上是我正在尝试但没有成功的事情 myvariable mysql database u user p password SELECT A B C FROM table a 正如你所看
  • Presto 数组包含一个喜欢某种模式的元素

    例如 我的表中的一列是一个数组 我想检查该列是否包含包含子字符串 denied 的元素 因此 在中午 12 00 拒绝 被管理员拒绝 等元素都将计数 我相信我将不得不使用 like 来识别模式 这个sql该怎么写呢 使用急板数组函数 htt
  • Oracle中表的列重新排序

    我有一个包含 50 多列的表 我需要交换前两列的顺序 使用 Oracle 实现此目的的最佳方法是什么 假设表名是 ORDERDETAILS 前两列是 ITEM ID 和 ORDER ID 重命名完成后 表名仍应为 ORDERDETAILS
  • 使用临时表替换 WHERE IN 子句

    我让用户输入我需要在表中查询的值列表 该列表可能非常大 并且长度在编译时未知 而不是使用WHERE IN 我认为使用临时表并对其执行联接会更有效 我在另一个SO问题中读到了这个建议 目前找不到它 但会在找到时进行编辑 要点是这样的 CREA
  • 在自定义条件下清理 SQL

    我需要创建一个简单的搜索 但我无法使用 Sphinx 这是我写的 keywords input split s queries keywords each do keyword queries lt lt sanitize sql for
  • Oracle Developer Tools for Visual Studio 2019 无法正确安装

    在 VS 2019 中 ODT 使用扩展名安装 而不是像以前的版本那样作为安装文件安装 因此 从 VS 2017 升级的 EF 6 使用的 MVC 项目 edmx 文件在扩展安装后不显示数据库图表 空白黑页 编辑 xml 选项等 仅此而已
  • 运行 Sqoop 导入和导出时如何找到最佳映射器数量?

    我正在使用 Sqoop 版本 1 4 2 和 Oracle 数据库 运行 Sqoop 命令时 例如这样 sqoop import fs
  • LEFT JOIN 返回与 INNER JOIN 相同的结果

    我有一张桌子 磨砂膏 里面有 1600 个独特的物品 第二张桌子有100万以上 我运行 INNER JOIN 并获得 65 个匹配项 SELECT a BW Parent Number a Vendor Name b Parent Supp
  • 帮助将二进制图像数据从 SQL Server 读取到 PHP 中

    我似乎无法找到将二进制数据从 SQL 服务器读取到 PHP 的方法 我正在开发一个项目 需要能够将图像直接存储在 SQL 表中 而不是文件系统上 目前 我一直在使用这样的查询 插入 myTable 文档 选择 从 OPENROWSET BU
  • MySQL如何进行浮点加法的数学计算?

    我测试过SELECT 0 1 0 2 用MySQL MariaDB 查询 它返回了正确的答案 MariaDB none gt SELECT 0 1 0 2 0 1 0 2 0 3 1 row in set 0 000 sec 在大多数编程语
  • T-SQL 相当于 =rand()

    我有几个内容表 我想用随机的文本段落填充它们 在 MS Word 中 我只需输入 rand 即可 我收到三段新鲜的文字 是否有 SQL 脚本 命令可用于使用 t sql 生成随机字典单词 declare Lorem nvarchar max
  • 如何防止用户生成的 Sql 查询上的 Sql 注入

    我有一个项目 私有的 ASP net 网站 受 https 密码保护 其中要求之一是用户能够输入直接查询数据库的 Sql 查询 我需要能够允许这些查询 同时防止它们对数据库本身造成损坏 以及访问或更新它们不应该访问 更新的数据 我制定了以下
  • 在旧版本的 MySQL (<5.5.0) 中模拟 TO_SECONDS()

    出于性能和简单性的原因 我想以秒的形式获取 MySQL 3 x 服务器中 DATETIME 列的内容 或者实际上任何数字类型 我只是想在使用 UNIX TIMESTAMP 时避免所有明显的时区问题 the我表中的日期确实来自不同的区域设置

随机推荐